ysknsid25/eslint-plugin-hono
🔥 ESLint plugin for Hono and all Hono lovers
eslint-plugin-hono
🔥 ESLint plugin for Hono and all Hono lovers
⚠️ Still in alpha version ⚠️
In order for this plugin to become a major release, it's extremely important that you experiment with it.
Please try it out and incorporate it into your Hono project.
Please also create issues with any bugs, improvement requests, and new feature suggestions.
Installation
npm install -D eslint-plugin-hono@alphaUsage (Flat Config)
To use the recommended configuration, create an eslint.config.js file in your project root and add the following:
import globals from "globals";
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
import hono from "eslint-plugin-hono";
export default [
{
plugins: {
hono: hono,
},
},
pluginJs.configs.recommended,
...tseslint.configs.recommended,
...hono.configs.recommended,
{
files: ["**/*.{ts,tsx,cts,mts}"],
languageOptions: {
parser: tseslint.parser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
project: "./tsconfig.json",
},
globals: globals.node,
},
},
];If you want to apply the rules only to specific files, you can use the files property:
import globals from "globals";
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
import hono from "eslint-plugin-hono";
export default [
{
plugins: {
hono: hono,
},
},
pluginJs.configs.recommended,
...tseslint.configs.recommended,
...hono.configs.recommended,
{
files: ["**/*.{ts,tsx,cts,mts}"],
languageOptions: {
parser: tseslint.parser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
project: "./tsconfig.json",
},
globals: globals.node,
},
rules: {
// custom rules
}
},
];Rules
| Rule | 🚨 error | 🔧 fix | |
|---|---|---|---|
| route-grouping | ✅ | ✅ | |
| prefer-http-exception | ✅ | ||
| param-name-mismatch | ✅ | ||
| no-multiple-next | ✅ | ||
| no-unused-context-response | ✅ | ||
| no-process-env | ✅ | ||
| global-middleware-placement | ✅ |
hono/route-grouping
Enforce grouping and ordering of routes by HTTP method and Hono instance.
This rule enhances code organization by checking three things:
- Instance Grouping: All route definitions for a specific Hono instance must be contiguous. Once you start defining routes for another instance, you cannot add more routes to the previous one.
- Path Grouping: Routes for the same path (e.g.,
/users) must be grouped together. - Method Order: Within a path group, methods must follow a consistent order (e.g.,
getbeforepost).
Note: app.route() calls are excluded from these checks. Method chains (e.g., .get(...).post(...)) are exempt from method order checking.
Options
{
"hono/route-grouping": ["error", {
"order": [
"use",
"all",
"get",
"post",
"put",
"patch",
"delete",
"options",
"on"
]
}]
}order: (array of strings, optional) Specifies the desired order of HTTP methods. The default order is ["use", "all", "get", "post", "put", "patch", "delete", "options", "on"].
Examples
Incorrect Path Grouping
const app = new Hono();
app.get('/path1', (c) => c.text('get'));
app.get('/path2', (c) => c.text('get'));
app.post('/path1', (c) => c.text('post'));Correct
const app = new Hono();
app.get('/path1', (c) => c.text('get'));
app.post('/path1', (c) => c.text('post'));
app.get('/path2', (c) => c.text('get'));Incorrect Method Order
const app = new Hono();
app.post('/path1', (c) => c.text('post'));
app.get('/path1', (c) => c.text('get'));Correct
const app = new Hono();
app.get('/path1', (c) => c.text('get'));
app.post('/path1', (c) => c.text('post'));Incorrect Instance Grouping
const books = new Hono();
const users = new Hono();
books.get('/books', (c) => c.text('get books'));
users.get('/users', (c) => c.text('get users'));
books.post('/books', (c) => c.text('create book')); // Error: books routes should be togetherCorrect
const books = new Hono();
const users = new Hono();
books.get('/books', (c) => c.text('get books'));
books.post('/books', (c) => c.text('create book'));
users.get('/users', (c) => c.text('get users'));hono/prefer-http-exception
Suggest using HTTPException instead of generic Error for HTTP errors.
This rule detects when a standard Error is thrown with a message that corresponds to a standard HTTP status code (e.g., "Not Found", "Unauthorized"). In Hono applications, it is better to use HTTPException to return proper HTTP status codes.
Examples
Incorrect
throw new Error('Not Found');
throw new Error('Unauthorized');
throw new Error('Bad Request');Correct
import { HTTPException } from 'hono/http-exception';
throw new HTTPException(404, { message: 'Not Found' });
throw new HTTPException(401, { message: 'Unauthorized' });
throw new HTTPException(400, { message: 'Bad Request' });hono/param-name-mismatch
Ensure parameter name in c.req.param() matches the route definition.
This rule checks that the parameter names used in c.req.param('name') call inside a route handler match the parameters defined in the route path (e.g., /posts/:postId). This prevents runtime errors caused by typos or mismatched parameter names.
Examples
Incorrect
const app = new Hono();
app.get('/posts/:postId', (c) => {
const id = c.req.param('id'); // 'id' is not defined in '/posts/:postId'
return c.text(id);
});Correct
const app = new Hono();
app.get('/posts/:postId', (c) => {
const postId = c.req.param('postId');
return c.text(postId);
});hono/no-multiple-next
Disallow multiple calls to next() in a single middleware execution path.
Hono middleware relies on await next() to pass control to the next middleware. Calling next() multiple times in the same middleware function will cause a runtime error ("next() called multiple times"). This rule detects and prevents such patterns.
Examples
Incorrect
const middleware = async (c, next) => {
await next();
await next(); // Error
};const middleware = async (c, next) => {
if (condition) {
await next();
}
await next(); // Error if condition is true
};Correct
const middleware = async (c, next) => {
await next();
};const middleware = async (c, next) => {
if (condition) {
await next();
} else {
await next();
}
};hono/no-unused-context-response
Disallow unused calls to Context response methods (c.json, c.text, etc.).
In Hono, methods like c.json() create a response object but do not send it automatically. If the return value is not returned from the handler (or awaited/used), the request might hang or result in a 404.
Examples
Incorrect
app.get('/', (c) => {
c.json({ message: 'hello' }); // return is missing!
});Correct
app.get('/', (c) => {
return c.json({ message: 'hello' });
});hono/no-process-env
Disallow the use of process.env in favor of c.env.
This rule enforces the use of c.env for accessing environment variables within Hono handlers instead of process.env. Using c.env ensures your application remains platform-agnostic, as it abstracts away environment-specific details (e.g., Cloudflare Workers bindings vs. Node.js process.env).
Examples
Incorrect
const app = new Hono();
app.get('/', (c) => {
const apiKey = process.env.API_KEY; // Disallowed
return c.text(apiKey);
});Correct
const app = new Hono();
app.get('/', (c) => {
const apiKey = c.env.API_KEY;
return c.text(apiKey);
});hono/global-middleware-placement
Enforce that global middleware is placed before route definitions.
This rule ensures that global middleware (e.g., app.use(logger) or app.use('*', logger)) is defined immediately after the Hono instance is created, and before any routes (app.get(), app.post(), etc.) are defined. This improves code readability and predictability. Path-specific middleware (e.g., app.use('/admin/*', adminAuth)) is ignored by this rule to allow for logical grouping with the routes it applies to.
Examples
Incorrect
const app = new Hono();
app.get('/', (c) => c.text('Hello'));
app.use('*', logger()); // Global middleware defined after a route.Correct
const app = new Hono();
app.use('*', logger());
app.get('/', (c) => c.text('Hello'));
app.use('/admin', adminOnly()); // Path-specific middleware can be defined later.
app.get('/admin/dashboard', (c) => c.text('Dashboard'));License
Made by Kanon. Publish under MIT License.