@hono/cli 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +275 -26
- package/dist/cli.js +552 -58
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Hono CLI
|
|
2
2
|
|
|
3
|
-
CLI for Hono
|
|
3
|
+
Hono CLI is a CLI for Humans and AI who use Hono.
|
|
4
|
+
|
|
5
|
+
It's not a `create-*` command, not only for dev, build, and deploy, but also not a Vite wrapper. Built on an entirely new concept.
|
|
6
|
+
|
|
7
|
+
Hono CLI will give you the `hono` command. For Humans, you can use sub-commands specialized for Hono for simple usages. For AI, providing sub-commands to build your Hono application efficiently with AI coding.
|
|
4
8
|
|
|
5
9
|
## Installation
|
|
6
10
|
|
|
@@ -14,59 +18,304 @@ npm install -g @hono/cli
|
|
|
14
18
|
# Show help
|
|
15
19
|
hono --help
|
|
16
20
|
|
|
17
|
-
#
|
|
18
|
-
hono create
|
|
19
|
-
|
|
20
|
-
# Open documentation
|
|
21
|
+
# Display documentation
|
|
21
22
|
hono docs
|
|
23
|
+
|
|
24
|
+
# Search documentation
|
|
25
|
+
hono search middleware
|
|
26
|
+
|
|
27
|
+
# Send request to Hono app
|
|
28
|
+
hono request
|
|
29
|
+
|
|
30
|
+
# Start server
|
|
31
|
+
hono serve
|
|
32
|
+
|
|
33
|
+
# Generate an optimized Hono app
|
|
34
|
+
hono optimize
|
|
22
35
|
```
|
|
23
36
|
|
|
24
37
|
## Commands
|
|
25
38
|
|
|
26
|
-
- `
|
|
27
|
-
- `
|
|
39
|
+
- `docs [path]` - Display Hono documentation
|
|
40
|
+
- `search <query>` - Search Hono documentation
|
|
41
|
+
- `request [file]` - Send request to Hono app using `app.request()`
|
|
42
|
+
- `serve [entry]` - Start server for Hono app
|
|
43
|
+
- `optimize [entry]` - Generate an optimized Hono app
|
|
28
44
|
|
|
29
|
-
### `
|
|
45
|
+
### `docs`
|
|
30
46
|
|
|
31
|
-
|
|
47
|
+
Display Hono documentation content directly in your terminal.
|
|
32
48
|
|
|
33
49
|
```bash
|
|
34
|
-
hono
|
|
50
|
+
hono docs [path]
|
|
35
51
|
```
|
|
36
52
|
|
|
37
53
|
**Arguments:**
|
|
38
54
|
|
|
39
|
-
- `
|
|
55
|
+
- `path` - Documentation path (optional)
|
|
56
|
+
|
|
57
|
+
**Examples:**
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Display main documentation summary (llms.txt)
|
|
61
|
+
hono docs
|
|
62
|
+
|
|
63
|
+
# Display specific documentation pages
|
|
64
|
+
hono docs /docs/concepts/motivation
|
|
65
|
+
hono docs /docs/guides/best-practices
|
|
66
|
+
hono docs /docs/api/context
|
|
67
|
+
|
|
68
|
+
# Display examples and tutorials
|
|
69
|
+
hono docs /examples/stytch-auth
|
|
70
|
+
hono docs /examples/basic
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### `search`
|
|
74
|
+
|
|
75
|
+
Search through Hono documentation using fuzzy search powered by Algolia.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
hono search <query> [options]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Arguments:**
|
|
82
|
+
|
|
83
|
+
- `query` - Search query (required)
|
|
40
84
|
|
|
41
85
|
**Options:**
|
|
42
86
|
|
|
43
|
-
- `-
|
|
44
|
-
- `-
|
|
45
|
-
- `-p, --pm <pm>` - Package manager to use (npm, bun, deno, pnpm, yarn)
|
|
46
|
-
- `-o, --offline` - Use offline mode
|
|
87
|
+
- `-l, --limit <number>` - Number of results to show (default: 5, max: 20)
|
|
88
|
+
- `-p, --pretty` - Display results in human-readable format (default: JSON output)
|
|
47
89
|
|
|
48
90
|
**Examples:**
|
|
49
91
|
|
|
50
92
|
```bash
|
|
51
|
-
#
|
|
52
|
-
hono
|
|
93
|
+
# Search for middleware documentation (JSON output by default)
|
|
94
|
+
hono search middleware
|
|
53
95
|
|
|
54
|
-
#
|
|
55
|
-
hono
|
|
96
|
+
# Search with pretty formatting for human-readable output
|
|
97
|
+
hono search middleware --pretty
|
|
98
|
+
|
|
99
|
+
# Search with custom result limit
|
|
100
|
+
hono search "getting started" --limit 10
|
|
101
|
+
```
|
|
56
102
|
|
|
57
|
-
|
|
58
|
-
hono create my-app --template cloudflare-workers
|
|
103
|
+
**Output Format:**
|
|
59
104
|
|
|
60
|
-
|
|
61
|
-
|
|
105
|
+
By default, the command outputs JSON for easy integration with other tools:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"query": "middleware",
|
|
110
|
+
"total": 5,
|
|
111
|
+
"results": [
|
|
112
|
+
{
|
|
113
|
+
"title": "Middleware ",
|
|
114
|
+
"category": "",
|
|
115
|
+
"url": "https://hono.dev/docs/guides/middleware#middleware",
|
|
116
|
+
"path": "/docs/guides/middleware"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"title": "Third-party Middleware - Hono",
|
|
120
|
+
"category": "Middleware",
|
|
121
|
+
"url": "https://hono.dev/docs/middleware/third-party#VPSidebarNav",
|
|
122
|
+
"path": "/docs/middleware/third-party"
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}
|
|
62
126
|
```
|
|
63
127
|
|
|
64
|
-
|
|
128
|
+
With the `--pretty` flag, results are displayed in a human-readable format:
|
|
65
129
|
|
|
66
|
-
|
|
130
|
+
```
|
|
131
|
+
1. Middleware
|
|
132
|
+
URL: https://hono.dev/docs/guides/middleware#middleware
|
|
133
|
+
Command: hono docs /docs/guides/middleware
|
|
134
|
+
|
|
135
|
+
2. Third-party Middleware - Hono
|
|
136
|
+
Category: Middleware
|
|
137
|
+
URL: https://hono.dev/docs/middleware/third-party#VPSidebarNav
|
|
138
|
+
Command: hono docs /docs/middleware/third-party
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### `request`
|
|
142
|
+
|
|
143
|
+
Send HTTP requests to your Hono application using the built-in `app.request()` method. This is particularly useful for testing and development.
|
|
67
144
|
|
|
68
145
|
```bash
|
|
69
|
-
hono
|
|
146
|
+
hono request [file] [options]
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Arguments:**
|
|
150
|
+
|
|
151
|
+
- `file` - Path to the Hono app file (TypeScript/JSX supported, optional)
|
|
152
|
+
|
|
153
|
+
**Options:**
|
|
154
|
+
|
|
155
|
+
- `-P, --path <path>` - Request path (default: "/")
|
|
156
|
+
- `-X, --method <method>` - HTTP method (default: GET)
|
|
157
|
+
- `-d, --data <data>` - Request body data
|
|
158
|
+
- `-H, --header <header>` - Custom headers (can be used multiple times)
|
|
159
|
+
- `-w, --watch` - Watch for changes and resend request
|
|
160
|
+
|
|
161
|
+
**Examples:**
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# GET request to default app root (uses src/index.ts or src/index.tsx)
|
|
165
|
+
hono request
|
|
166
|
+
|
|
167
|
+
# GET request to specific path
|
|
168
|
+
hono request -P /users/123
|
|
169
|
+
|
|
170
|
+
# POST request with data
|
|
171
|
+
hono request -P /api/users -X POST -d '{"name":"Alice"}'
|
|
172
|
+
|
|
173
|
+
# Request to specific file
|
|
174
|
+
hono request -P /api src/your-app.ts
|
|
175
|
+
|
|
176
|
+
# Request with custom headers
|
|
177
|
+
hono request -P /api/protected \
|
|
178
|
+
-H 'Authorization: Bearer token' \
|
|
179
|
+
-H 'User-Agent: MyApp' \
|
|
180
|
+
src/your-app.ts
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Response Format:**
|
|
184
|
+
|
|
185
|
+
The command returns a JSON object with the following structure:
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{
|
|
189
|
+
"status": 200,
|
|
190
|
+
"body": "{\"message\":\"Hello World\"}",
|
|
191
|
+
"headers": {
|
|
192
|
+
"content-type": "application/json",
|
|
193
|
+
"x-custom-header": "value"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### `serve`
|
|
199
|
+
|
|
200
|
+
Start a server for your Hono application. This is a simple server specialized for Hono applications with built-in TypeScript and JSX support.
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
hono serve [entry] [options]
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Arguments:**
|
|
207
|
+
|
|
208
|
+
- `entry` - Entry file for your Hono app (TypeScript/JSX supported, optional)
|
|
209
|
+
|
|
210
|
+
**Options:**
|
|
211
|
+
|
|
212
|
+
- `-p, --port <port>` - Port number (default: 7070)
|
|
213
|
+
- `--show-routes` - Show registered routes
|
|
214
|
+
- `--use <middleware>` - Use middleware (can be used multiple times)
|
|
215
|
+
|
|
216
|
+
**Examples:**
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Start server with default empty app (no entry file needed)
|
|
220
|
+
hono serve
|
|
221
|
+
|
|
222
|
+
# Start server on specific port
|
|
223
|
+
hono serve -p 3000 src/app.ts
|
|
224
|
+
|
|
225
|
+
# Start server with specific entry file
|
|
226
|
+
hono serve src/app.ts
|
|
227
|
+
|
|
228
|
+
# Start server and show routes
|
|
229
|
+
hono serve --show-routes src/app.ts
|
|
230
|
+
|
|
231
|
+
# Start server with middleware
|
|
232
|
+
hono serve --use 'cors()' src/app.ts
|
|
233
|
+
|
|
234
|
+
# Start server with multiple middleware
|
|
235
|
+
hono serve --use 'cors()' --use 'logger()' src/app.ts
|
|
236
|
+
|
|
237
|
+
# Start server with authentication and static file serving
|
|
238
|
+
hono serve \
|
|
239
|
+
--use 'basicAuth({ username: "foo", password: "bar" })' \
|
|
240
|
+
--use "serveStatic({ root: './' })"
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### `optimize`
|
|
244
|
+
|
|
245
|
+
Generate an optimized Hono class and export bundled file.
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
hono optimize [entry] [options]
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Arguments:**
|
|
252
|
+
|
|
253
|
+
- `entry` - Entry file for your Hono app (TypeScript/JSX supported, optional)
|
|
254
|
+
|
|
255
|
+
**Options:**
|
|
256
|
+
|
|
257
|
+
- `-o, --outfile <outfile>` - Output file
|
|
258
|
+
|
|
259
|
+
**Examples:**
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
# Generate an optimized Hono class and export bundled file to dist/index.js
|
|
263
|
+
hono optimize
|
|
264
|
+
|
|
265
|
+
# Specify entry file and output file
|
|
266
|
+
hono optimize -o dist/app.js src/app.ts
|
|
267
|
+
|
|
268
|
+
# Export bundled file with minification
|
|
269
|
+
hono optimize -m
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Tips
|
|
273
|
+
|
|
274
|
+
### Using Hono CLI with AI Code Agents
|
|
275
|
+
|
|
276
|
+
When working with AI code agents like Claude Code, you can configure them to use the `hono` CLI for efficient documentation access and testing. Add the following to your project's `CLAUDE.md` or similar configuration:
|
|
277
|
+
|
|
278
|
+
````markdown
|
|
279
|
+
## Hono Development
|
|
280
|
+
|
|
281
|
+
Use the `hono` CLI for efficient development. View all commands with `hono --help`.
|
|
282
|
+
|
|
283
|
+
### Core Commands
|
|
284
|
+
|
|
285
|
+
- **`hono docs [path]`** - Browse Hono documentation
|
|
286
|
+
- **`hono search <query>`** - Search documentation
|
|
287
|
+
- **`hono request [file]`** - Test app requests without starting a server
|
|
288
|
+
|
|
289
|
+
### Quick Examples
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Search for topics
|
|
293
|
+
hono search middleware
|
|
294
|
+
hono search "getting started"
|
|
295
|
+
|
|
296
|
+
# View documentation
|
|
297
|
+
hono docs /docs/api/context
|
|
298
|
+
hono docs /docs/guides/middleware
|
|
299
|
+
|
|
300
|
+
# Test your app
|
|
301
|
+
hono request -P /api/users src/index.ts
|
|
302
|
+
hono request -P /api/users -X POST -d '{"name":"Alice"}' src/index.ts
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Workflow
|
|
306
|
+
|
|
307
|
+
1. Search documentation: `hono search <query>`
|
|
308
|
+
2. Read relevant docs: `hono docs [path]`
|
|
309
|
+
3. Test implementation: `hono request [file]`
|
|
310
|
+
````
|
|
311
|
+
|
|
312
|
+
### Pipeline Integration with jq
|
|
313
|
+
|
|
314
|
+
The `search` command outputs JSON by default, making it easy to pipe results to other commands:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
# Search and view documentation in one command
|
|
318
|
+
hono search "middleware" | jq '.results[0].path' | hono docs
|
|
70
319
|
```
|
|
71
320
|
|
|
72
321
|
## Authors
|
package/dist/cli.js
CHANGED
|
@@ -3,84 +3,578 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { readFileSync } from "fs";
|
|
6
|
-
import { dirname, join } from "path";
|
|
6
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
|
|
9
|
-
// src/commands/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
// src/commands/docs/index.ts
|
|
10
|
+
async function fetchAndDisplayContent(url, fallbackUrl) {
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetch(url);
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
throw new Error(`Failed to fetch documentation: ${response.status} ${response.statusText}`);
|
|
15
|
+
}
|
|
16
|
+
const content = await response.text();
|
|
17
|
+
console.log("\n" + content);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error(
|
|
20
|
+
"Error fetching documentation:",
|
|
21
|
+
error instanceof Error ? error.message : String(error)
|
|
22
|
+
);
|
|
23
|
+
console.log(`
|
|
24
|
+
Please visit: ${fallbackUrl || "https://hono.dev/docs"}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function docsCommand(program2) {
|
|
28
|
+
program2.command("docs").argument(
|
|
29
|
+
"[path]",
|
|
30
|
+
"Documentation path (e.g., /docs/concepts/motivation, /examples/stytch-auth)",
|
|
31
|
+
""
|
|
32
|
+
).description("Display Hono documentation").action(async (path) => {
|
|
33
|
+
let finalPath = path;
|
|
34
|
+
if (!path) {
|
|
35
|
+
if (!process.stdin.isTTY) {
|
|
36
|
+
try {
|
|
37
|
+
const chunks = [];
|
|
38
|
+
for await (const chunk of process.stdin) {
|
|
39
|
+
chunks.push(chunk);
|
|
40
|
+
}
|
|
41
|
+
const stdinInput = Buffer.concat(chunks).toString().trim();
|
|
42
|
+
if (stdinInput) {
|
|
43
|
+
finalPath = stdinInput.replace(/^["'](.*)["']$/, "$1");
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error("Error reading from stdin:", error);
|
|
47
|
+
}
|
|
20
48
|
}
|
|
21
|
-
if (
|
|
22
|
-
|
|
49
|
+
if (!finalPath) {
|
|
50
|
+
console.log("Fetching Hono documentation...");
|
|
51
|
+
await fetchAndDisplayContent("https://hono.dev/llms.txt");
|
|
52
|
+
return;
|
|
23
53
|
}
|
|
24
|
-
|
|
25
|
-
|
|
54
|
+
}
|
|
55
|
+
const normalizedPath = finalPath.startsWith("/") ? finalPath : `/${finalPath}`;
|
|
56
|
+
const basePath = normalizedPath.slice(1);
|
|
57
|
+
const markdownUrl = `https://raw.githubusercontent.com/honojs/website/refs/heads/main/${basePath}.md`;
|
|
58
|
+
const webUrl = `https://hono.dev${normalizedPath}`;
|
|
59
|
+
console.log(`Fetching Hono documentation for ${finalPath}...`);
|
|
60
|
+
await fetchAndDisplayContent(markdownUrl, webUrl);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/commands/optimize/index.ts
|
|
65
|
+
import * as esbuild2 from "esbuild";
|
|
66
|
+
import { buildInitParams, serializeInitParams } from "hono/router/reg-exp-router";
|
|
67
|
+
import { execFile } from "child_process";
|
|
68
|
+
import { existsSync, realpathSync, statSync } from "fs";
|
|
69
|
+
import { dirname, join, resolve } from "path";
|
|
70
|
+
|
|
71
|
+
// src/utils/build.ts
|
|
72
|
+
import * as esbuild from "esbuild";
|
|
73
|
+
async function* buildAndImportApp(filePath, options = {}) {
|
|
74
|
+
let resolveApp;
|
|
75
|
+
let appPromise;
|
|
76
|
+
const preparePromise = () => {
|
|
77
|
+
appPromise = new Promise((resolve4) => {
|
|
78
|
+
resolveApp = resolve4;
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
preparePromise();
|
|
82
|
+
const context2 = await esbuild.context({
|
|
83
|
+
entryPoints: [filePath],
|
|
84
|
+
bundle: true,
|
|
85
|
+
write: false,
|
|
86
|
+
format: "esm",
|
|
87
|
+
target: "node20",
|
|
88
|
+
jsx: "automatic",
|
|
89
|
+
jsxImportSource: "hono/jsx",
|
|
90
|
+
platform: "node",
|
|
91
|
+
external: options.external || [],
|
|
92
|
+
plugins: [
|
|
93
|
+
{
|
|
94
|
+
name: "watch",
|
|
95
|
+
setup(build2) {
|
|
96
|
+
build2.onEnd(async (result) => {
|
|
97
|
+
try {
|
|
98
|
+
const code = result.outputFiles?.[0]?.text || "";
|
|
99
|
+
const dataUrl = `data:text/javascript;base64,${Buffer.from(code).toString("base64")}`;
|
|
100
|
+
const module = await import(dataUrl);
|
|
101
|
+
const app = module.default;
|
|
102
|
+
if (!app) {
|
|
103
|
+
throw new Error("Failed to build app");
|
|
104
|
+
}
|
|
105
|
+
if (!app || typeof app.request !== "function") {
|
|
106
|
+
throw new Error("No valid Hono app exported from the file");
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
resolveApp(app);
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error("Error building app", error);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
26
117
|
}
|
|
27
|
-
|
|
28
|
-
|
|
118
|
+
]
|
|
119
|
+
});
|
|
120
|
+
await context2.watch();
|
|
121
|
+
if (!options.watch) {
|
|
122
|
+
await context2.dispose();
|
|
123
|
+
}
|
|
124
|
+
do {
|
|
125
|
+
yield await appPromise;
|
|
126
|
+
preparePromise();
|
|
127
|
+
} while (options.watch);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/commands/optimize/index.ts
|
|
131
|
+
var DEFAULT_ENTRY_CANDIDATES = ["src/index.ts", "src/index.tsx", "src/index.js", "src/index.jsx"];
|
|
132
|
+
function optimizeCommand(program2) {
|
|
133
|
+
program2.command("optimize").description("Build optimized Hono class").argument("[entry]", "entry file").option("-o, --outfile [outfile]", "output file", "dist/index.js").option("-m, --minify", "minify output file").action(async (entry, options) => {
|
|
134
|
+
if (!entry) {
|
|
135
|
+
entry = DEFAULT_ENTRY_CANDIDATES.find((entry2) => existsSync(entry2)) ?? DEFAULT_ENTRY_CANDIDATES[0];
|
|
136
|
+
}
|
|
137
|
+
const appPath = resolve(process.cwd(), entry);
|
|
138
|
+
if (!existsSync(appPath)) {
|
|
139
|
+
throw new Error(`Entry file ${entry} does not exist`);
|
|
140
|
+
}
|
|
141
|
+
const appFilePath = realpathSync(appPath);
|
|
142
|
+
const buildIterator = buildAndImportApp(appFilePath, {
|
|
143
|
+
external: ["@hono/node-server"]
|
|
144
|
+
});
|
|
145
|
+
const app = (await buildIterator.next()).value;
|
|
146
|
+
let routerName;
|
|
147
|
+
let importStatement;
|
|
148
|
+
let assignRouterStatement;
|
|
149
|
+
try {
|
|
150
|
+
const serialized = serializeInitParams(
|
|
151
|
+
buildInitParams({
|
|
152
|
+
paths: app.routes.map(({ path }) => path)
|
|
153
|
+
})
|
|
154
|
+
);
|
|
155
|
+
const hasPreparedRegExpRouter = await new Promise((resolve4) => {
|
|
156
|
+
const child = execFile(process.execPath, [
|
|
157
|
+
"--input-type=module",
|
|
158
|
+
"-e",
|
|
159
|
+
"try { (await import('hono/router/reg-exp-router')).PreparedRegExpRouter && process.exit(0) } finally { process.exit(1) }"
|
|
160
|
+
]);
|
|
161
|
+
child.on("exit", (code) => {
|
|
162
|
+
resolve4(code === 0);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
if (hasPreparedRegExpRouter) {
|
|
166
|
+
routerName = "PreparedRegExpRouter";
|
|
167
|
+
importStatement = "import { PreparedRegExpRouter } from 'hono/router/reg-exp-router'";
|
|
168
|
+
assignRouterStatement = `const routerParams = ${serialized}
|
|
169
|
+
this.router = new PreparedRegExpRouter(...routerParams)`;
|
|
170
|
+
} else {
|
|
171
|
+
routerName = "RegExpRouter";
|
|
172
|
+
importStatement = "import { RegExpRouter } from 'hono/router/reg-exp-router'";
|
|
173
|
+
assignRouterStatement = "this.router = new RegExpRouter()";
|
|
29
174
|
}
|
|
30
|
-
|
|
31
|
-
|
|
175
|
+
} catch {
|
|
176
|
+
routerName = "TrieRouter";
|
|
177
|
+
importStatement = "import { TrieRouter } from 'hono/router/trie-router'";
|
|
178
|
+
assignRouterStatement = "this.router = new TrieRouter()";
|
|
179
|
+
}
|
|
180
|
+
console.log("[Optimized]");
|
|
181
|
+
console.log(` Router: ${routerName}`);
|
|
182
|
+
const outfile = resolve(process.cwd(), options.outfile);
|
|
183
|
+
await esbuild2.build({
|
|
184
|
+
entryPoints: [appFilePath],
|
|
185
|
+
outfile,
|
|
186
|
+
bundle: true,
|
|
187
|
+
minify: options.minify,
|
|
188
|
+
format: "esm",
|
|
189
|
+
target: "node20",
|
|
190
|
+
platform: "node",
|
|
191
|
+
jsx: "automatic",
|
|
192
|
+
jsxImportSource: "hono/jsx",
|
|
193
|
+
plugins: [
|
|
194
|
+
{
|
|
195
|
+
name: "hono-optimize",
|
|
196
|
+
setup(build2) {
|
|
197
|
+
const honoPseudoImportPath = "hono-optimized-pseudo-import-path";
|
|
198
|
+
build2.onResolve({ filter: /^hono$/ }, async (args) => {
|
|
199
|
+
if (!args.importer) {
|
|
200
|
+
return void 0;
|
|
201
|
+
}
|
|
202
|
+
const resolved = await build2.resolve(args.path, {
|
|
203
|
+
kind: "import-statement",
|
|
204
|
+
resolveDir: args.resolveDir
|
|
205
|
+
});
|
|
206
|
+
return {
|
|
207
|
+
path: join(dirname(resolved.path), honoPseudoImportPath)
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
build2.onLoad({ filter: new RegExp(`/${honoPseudoImportPath}$`) }, async () => {
|
|
211
|
+
return {
|
|
212
|
+
contents: `
|
|
213
|
+
import { HonoBase } from 'hono/hono-base'
|
|
214
|
+
${importStatement}
|
|
215
|
+
export class Hono extends HonoBase {
|
|
216
|
+
constructor(options = {}) {
|
|
217
|
+
super(options)
|
|
218
|
+
${assignRouterStatement}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
`
|
|
222
|
+
};
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
});
|
|
228
|
+
const outfileStat = statSync(outfile);
|
|
229
|
+
console.log(` Output: ${options.outfile} (${(outfileStat.size / 1024).toFixed(2)} KB)`);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/commands/request/index.ts
|
|
234
|
+
import { existsSync as existsSync2, realpathSync as realpathSync2 } from "fs";
|
|
235
|
+
import { resolve as resolve2 } from "path";
|
|
236
|
+
var DEFAULT_ENTRY_CANDIDATES2 = ["src/index.ts", "src/index.tsx", "src/index.js", "src/index.jsx"];
|
|
237
|
+
function requestCommand(program2) {
|
|
238
|
+
program2.command("request").description("Send request to Hono app using app.request()").argument("[file]", "Path to the Hono app file").option("-P, --path <path>", "Request path", "/").option("-X, --method <method>", "HTTP method", "GET").option("-d, --data <data>", "Request body data").option("-w, --watch", "Watch for changes and re-run the request", false).option(
|
|
239
|
+
"-H, --header <header>",
|
|
240
|
+
"Custom headers",
|
|
241
|
+
(value, previous) => {
|
|
242
|
+
return previous ? [...previous, value] : [value];
|
|
243
|
+
},
|
|
244
|
+
[]
|
|
245
|
+
).action(async (file, options) => {
|
|
246
|
+
const path = options.path || "/";
|
|
247
|
+
const watch = options.watch;
|
|
248
|
+
const buildIterator = getBuildIterator(file, watch);
|
|
249
|
+
for await (const app of buildIterator) {
|
|
250
|
+
const result = await executeRequest(app, path, options);
|
|
251
|
+
console.log(JSON.stringify(result, null, 2));
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
function getBuildIterator(appPath, watch) {
|
|
256
|
+
let entry;
|
|
257
|
+
let resolvedAppPath;
|
|
258
|
+
if (appPath) {
|
|
259
|
+
entry = appPath;
|
|
260
|
+
resolvedAppPath = resolve2(process.cwd(), entry);
|
|
261
|
+
} else {
|
|
262
|
+
entry = DEFAULT_ENTRY_CANDIDATES2.find((candidate) => existsSync2(resolve2(process.cwd(), candidate))) ?? DEFAULT_ENTRY_CANDIDATES2[0];
|
|
263
|
+
resolvedAppPath = resolve2(process.cwd(), entry);
|
|
264
|
+
}
|
|
265
|
+
if (!existsSync2(resolvedAppPath)) {
|
|
266
|
+
throw new Error(`Entry file ${entry} does not exist`);
|
|
267
|
+
}
|
|
268
|
+
const appFilePath = realpathSync2(resolvedAppPath);
|
|
269
|
+
return buildAndImportApp(appFilePath, {
|
|
270
|
+
external: ["@hono/node-server"],
|
|
271
|
+
watch
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
async function executeRequest(app, requestPath, options) {
|
|
275
|
+
const url = new URL(requestPath, "http://localhost");
|
|
276
|
+
const requestInit = {
|
|
277
|
+
method: options.method || "GET"
|
|
278
|
+
};
|
|
279
|
+
if (options.data) {
|
|
280
|
+
requestInit.body = options.data;
|
|
281
|
+
}
|
|
282
|
+
if (options.header && options.header.length > 0) {
|
|
283
|
+
const headers = new Headers();
|
|
284
|
+
for (const header of options.header) {
|
|
285
|
+
const [key, value] = header.split(":", 2);
|
|
286
|
+
if (key && value) {
|
|
287
|
+
headers.set(key.trim(), value.trim());
|
|
32
288
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
289
|
+
}
|
|
290
|
+
requestInit.headers = headers;
|
|
291
|
+
}
|
|
292
|
+
const request = new Request(url.href, requestInit);
|
|
293
|
+
const response = await app.request(request);
|
|
294
|
+
const responseHeaders = {};
|
|
295
|
+
response.headers.forEach((value, key) => {
|
|
296
|
+
responseHeaders[key] = value;
|
|
297
|
+
});
|
|
298
|
+
const body = await response.text();
|
|
299
|
+
return {
|
|
300
|
+
status: response.status,
|
|
301
|
+
body,
|
|
302
|
+
headers: responseHeaders
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/commands/search/index.ts
|
|
307
|
+
function searchCommand(program2) {
|
|
308
|
+
program2.command("search").argument("<query>", "Search query for Hono documentation").option("-l, --limit <number>", "Number of results to show (default: 5)", (value) => {
|
|
309
|
+
const parsed = parseInt(value, 10);
|
|
310
|
+
if (isNaN(parsed) || parsed < 1 || parsed > 20) {
|
|
311
|
+
console.warn("Limit must be a number between 1 and 20\n");
|
|
312
|
+
return 5;
|
|
313
|
+
}
|
|
314
|
+
return parsed;
|
|
315
|
+
}).option("-p, --pretty", "Display results in human-readable format").description("Search Hono documentation").action(async (query, options) => {
|
|
316
|
+
const ALGOLIA_APP_ID = "1GIFSU1REV";
|
|
317
|
+
const ALGOLIA_API_KEY = "c6a0f86b9a9f8551654600f28317a9e9";
|
|
318
|
+
const ALGOLIA_INDEX = "hono";
|
|
319
|
+
const searchUrl = `https://${ALGOLIA_APP_ID}-dsn.algolia.net/1/indexes/${ALGOLIA_INDEX}/query`;
|
|
320
|
+
try {
|
|
321
|
+
if (options.pretty) {
|
|
322
|
+
console.log(`Searching for "${query}"...`);
|
|
323
|
+
}
|
|
324
|
+
const response = await fetch(searchUrl, {
|
|
325
|
+
method: "POST",
|
|
326
|
+
headers: {
|
|
327
|
+
"X-Algolia-API-Key": ALGOLIA_API_KEY,
|
|
328
|
+
"X-Algolia-Application-Id": ALGOLIA_APP_ID,
|
|
329
|
+
"Content-Type": "application/json"
|
|
330
|
+
},
|
|
331
|
+
body: JSON.stringify({
|
|
332
|
+
query,
|
|
333
|
+
hitsPerPage: options.limit || 5
|
|
334
|
+
})
|
|
39
335
|
});
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
336
|
+
if (!response.ok) {
|
|
337
|
+
throw new Error(`Search failed: ${response.status} ${response.statusText}`);
|
|
338
|
+
}
|
|
339
|
+
const data = await response.json();
|
|
340
|
+
if (data.hits.length === 0) {
|
|
341
|
+
if (options.pretty) {
|
|
342
|
+
console.log("\nNo results found.");
|
|
343
|
+
} else {
|
|
344
|
+
console.log(JSON.stringify({ query, total: 0, results: [] }, null, 2));
|
|
345
|
+
}
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const cleanHighlight = (text) => text.replace(/<[^>]*>/g, "");
|
|
349
|
+
const results = data.hits.map((hit) => {
|
|
350
|
+
let title = hit.title;
|
|
351
|
+
let highlightedTitle = title;
|
|
352
|
+
if (!title && hit._highlightResult?.hierarchy?.lvl1) {
|
|
353
|
+
title = cleanHighlight(hit._highlightResult.hierarchy.lvl1.value);
|
|
354
|
+
highlightedTitle = hit._highlightResult.hierarchy.lvl1.value;
|
|
355
|
+
}
|
|
356
|
+
if (!title) {
|
|
357
|
+
title = hit.hierarchy?.lvl1 || hit.hierarchy?.lvl0 || "Untitled";
|
|
358
|
+
highlightedTitle = title;
|
|
359
|
+
}
|
|
360
|
+
const hierarchyParts = [];
|
|
361
|
+
if (hit.hierarchy?.lvl0 && hit.hierarchy.lvl0 !== "Documentation") {
|
|
362
|
+
hierarchyParts.push(hit.hierarchy.lvl0);
|
|
363
|
+
}
|
|
364
|
+
if (hit.hierarchy?.lvl1 && hit.hierarchy.lvl1 !== title) {
|
|
365
|
+
hierarchyParts.push(cleanHighlight(hit.hierarchy.lvl1));
|
|
43
366
|
}
|
|
367
|
+
if (hit.hierarchy?.lvl2) {
|
|
368
|
+
hierarchyParts.push(cleanHighlight(hit.hierarchy.lvl2));
|
|
369
|
+
}
|
|
370
|
+
const category = hierarchyParts.length > 0 ? hierarchyParts.join(" > ") : "";
|
|
371
|
+
const url = hit.url;
|
|
372
|
+
const urlPath = new URL(url).pathname;
|
|
373
|
+
return {
|
|
374
|
+
title,
|
|
375
|
+
highlightedTitle,
|
|
376
|
+
category,
|
|
377
|
+
url,
|
|
378
|
+
path: urlPath
|
|
379
|
+
};
|
|
44
380
|
});
|
|
381
|
+
if (options.pretty) {
|
|
382
|
+
console.log(`
|
|
383
|
+
Found ${data.hits.length} results:
|
|
384
|
+
`);
|
|
385
|
+
const formatHighlight = (text) => {
|
|
386
|
+
return text.replace(/<span class="algolia-docsearch-suggestion--highlight">/g, "\x1B[33m").replace(/<\/span>/g, "\x1B[0m");
|
|
387
|
+
};
|
|
388
|
+
results.forEach((result, index) => {
|
|
389
|
+
console.log(`${index + 1}. ${formatHighlight(result.highlightedTitle || result.title)}`);
|
|
390
|
+
if (result.category) {
|
|
391
|
+
console.log(` Category: ${result.category}`);
|
|
392
|
+
}
|
|
393
|
+
console.log(` URL: ${result.url}`);
|
|
394
|
+
console.log(` Command: hono docs ${result.path}`);
|
|
395
|
+
console.log("");
|
|
396
|
+
});
|
|
397
|
+
} else {
|
|
398
|
+
const jsonResults = results.map(({ highlightedTitle, ...result }) => result);
|
|
399
|
+
console.log(
|
|
400
|
+
JSON.stringify(
|
|
401
|
+
{
|
|
402
|
+
query,
|
|
403
|
+
total: data.hits.length,
|
|
404
|
+
results: jsonResults
|
|
405
|
+
},
|
|
406
|
+
null,
|
|
407
|
+
2
|
|
408
|
+
)
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
} catch (error) {
|
|
412
|
+
console.error(
|
|
413
|
+
"Error searching documentation:",
|
|
414
|
+
error instanceof Error ? error.message : String(error)
|
|
415
|
+
);
|
|
416
|
+
console.log("\nPlease visit: https://hono.dev/docs");
|
|
45
417
|
}
|
|
46
|
-
);
|
|
418
|
+
});
|
|
47
419
|
}
|
|
48
420
|
|
|
49
|
-
// src/commands/
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
421
|
+
// src/commands/serve/index.ts
|
|
422
|
+
import { serve } from "@hono/node-server";
|
|
423
|
+
import { serveStatic } from "@hono/node-server/serve-static";
|
|
424
|
+
import { Hono } from "hono";
|
|
425
|
+
import { showRoutes } from "hono/dev";
|
|
426
|
+
import { existsSync as existsSync3, realpathSync as realpathSync3 } from "fs";
|
|
427
|
+
import { resolve as resolve3 } from "path";
|
|
428
|
+
|
|
429
|
+
// src/commands/serve/builtin-map.ts
|
|
430
|
+
var builtinMap = {
|
|
431
|
+
// Authentication
|
|
432
|
+
basicAuth: "hono/basic-auth",
|
|
433
|
+
bearerAuth: "hono/bearer-auth",
|
|
434
|
+
// Security & Validation
|
|
435
|
+
csrf: "hono/csrf",
|
|
436
|
+
secureHeaders: "hono/secure-headers",
|
|
437
|
+
jwt: "hono/jwt",
|
|
438
|
+
jwk: "hono/jwk",
|
|
439
|
+
// Request/Response Processing
|
|
440
|
+
cors: "hono/cors",
|
|
441
|
+
etag: "hono/etag",
|
|
442
|
+
compress: "hono/compress",
|
|
443
|
+
prettyJSON: "hono/pretty-json",
|
|
444
|
+
bodyLimit: "hono/body-limit",
|
|
445
|
+
combine: "hono/combine",
|
|
446
|
+
contextStorage: "hono/context-storage",
|
|
447
|
+
methodOverride: "hono/method-override",
|
|
448
|
+
trailingSlash: "hono/trailing-slash",
|
|
449
|
+
// Utilities & Performance
|
|
450
|
+
cache: "hono/cache",
|
|
451
|
+
timeout: "hono/timeout",
|
|
452
|
+
poweredBy: "hono/powered-by",
|
|
453
|
+
// Logging & Monitoring
|
|
454
|
+
logger: "hono/logger",
|
|
455
|
+
timing: "hono/timing",
|
|
456
|
+
requestId: "hono/request-id",
|
|
457
|
+
// Internationalization
|
|
458
|
+
language: "hono/language",
|
|
459
|
+
// Access Control
|
|
460
|
+
ipRestriction: "hono/ip-restriction",
|
|
461
|
+
// Rendering
|
|
462
|
+
jsxRenderer: "hono/jsx-renderer",
|
|
463
|
+
// Static Files (Node.js specific)
|
|
464
|
+
serveStatic: "@hono/node-server/serve-static",
|
|
465
|
+
// Helpers - Accepts
|
|
466
|
+
accepts: "hono/accepts",
|
|
467
|
+
// Helpers - Adapter
|
|
468
|
+
env: "hono/adapter",
|
|
469
|
+
getRuntimeKey: "hono/adapter",
|
|
470
|
+
// Helpers - Connection Info (Node.js specific)
|
|
471
|
+
getConnInfo: "@hono/node-server/conninfo",
|
|
472
|
+
// Helpers - Cookie
|
|
473
|
+
getCookie: "hono/cookie",
|
|
474
|
+
getSignedCookie: "hono/cookie",
|
|
475
|
+
setCookie: "hono/cookie",
|
|
476
|
+
setSignedCookie: "hono/cookie",
|
|
477
|
+
deleteCookie: "hono/cookie",
|
|
478
|
+
// Helpers - CSS
|
|
479
|
+
css: "hono/css",
|
|
480
|
+
rawCssString: "hono/css",
|
|
481
|
+
viewTransition: "hono/css",
|
|
482
|
+
createCssContext: "hono/css",
|
|
483
|
+
createCssMiddleware: "hono/css",
|
|
484
|
+
// Helpers - HTML
|
|
485
|
+
html: "hono/html",
|
|
486
|
+
raw: "hono/html",
|
|
487
|
+
// Helpers - JWT (different from middleware)
|
|
488
|
+
sign: "hono/jwt",
|
|
489
|
+
verify: "hono/jwt",
|
|
490
|
+
decode: "hono/jwt",
|
|
491
|
+
// Helpers - Proxy
|
|
492
|
+
proxy: "hono/proxy",
|
|
493
|
+
// Helpers - Streaming
|
|
494
|
+
stream: "hono/streaming",
|
|
495
|
+
streamText: "hono/streaming",
|
|
496
|
+
streamSSE: "hono/streaming"
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
// src/commands/serve/index.ts
|
|
500
|
+
[serveStatic].forEach((f) => {
|
|
501
|
+
if (typeof f === "function") {
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
function serveCommand(program2) {
|
|
505
|
+
program2.command("serve").description("Start server").argument("[entry]", "entry file").option("-p, --port <port>", "port number").option("--show-routes", "show registered routes").option(
|
|
506
|
+
"--use <middleware>",
|
|
507
|
+
"use middleware",
|
|
508
|
+
(value, previous) => {
|
|
509
|
+
return previous ? [...previous, value] : [value];
|
|
510
|
+
},
|
|
511
|
+
[]
|
|
512
|
+
).action(
|
|
513
|
+
async (entry, options) => {
|
|
514
|
+
let app;
|
|
515
|
+
if (!entry) {
|
|
516
|
+
app = new Hono();
|
|
71
517
|
} else {
|
|
72
|
-
|
|
518
|
+
const appPath = resolve3(process.cwd(), entry);
|
|
519
|
+
if (!existsSync3(appPath)) {
|
|
520
|
+
app = new Hono();
|
|
521
|
+
} else {
|
|
522
|
+
const appFilePath = realpathSync3(appPath);
|
|
523
|
+
const buildIterator = buildAndImportApp(appFilePath, {
|
|
524
|
+
external: ["@hono/node-server"]
|
|
525
|
+
});
|
|
526
|
+
app = (await buildIterator.next()).value;
|
|
527
|
+
}
|
|
73
528
|
}
|
|
74
|
-
|
|
75
|
-
|
|
529
|
+
const allFunctions = {};
|
|
530
|
+
const uniqueModules = [...new Set(Object.values(builtinMap))];
|
|
531
|
+
for (const modulePath of uniqueModules) {
|
|
532
|
+
try {
|
|
533
|
+
const module = await import(modulePath);
|
|
534
|
+
for (const [funcName, modulePathInMap] of Object.entries(builtinMap)) {
|
|
535
|
+
if (modulePathInMap === modulePath && module[funcName]) {
|
|
536
|
+
allFunctions[funcName] = module[funcName];
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
} catch (error) {
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
const baseApp = new Hono();
|
|
543
|
+
for (const use of options.use || []) {
|
|
544
|
+
const functionNames = Object.keys(allFunctions);
|
|
545
|
+
const functionValues = Object.values(allFunctions);
|
|
546
|
+
const func = new Function("c", "next", ...functionNames, `return (${use})`);
|
|
547
|
+
baseApp.use(async (c, next) => {
|
|
548
|
+
const middleware = func(c, next, ...functionValues);
|
|
549
|
+
return typeof middleware === "function" ? middleware(c, next) : middleware;
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
baseApp.route("/", app);
|
|
553
|
+
if (options.showRoutes) {
|
|
554
|
+
showRoutes(baseApp);
|
|
555
|
+
}
|
|
556
|
+
serve(
|
|
557
|
+
{
|
|
558
|
+
fetch: baseApp.fetch,
|
|
559
|
+
port: options.port ? Number.parseInt(options.port) : 7070
|
|
560
|
+
},
|
|
561
|
+
(info) => {
|
|
562
|
+
console.log(`Listening on http://localhost:${info.port}`);
|
|
563
|
+
}
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
);
|
|
76
567
|
}
|
|
77
568
|
|
|
78
569
|
// src/cli.ts
|
|
79
570
|
var __filename = fileURLToPath(import.meta.url);
|
|
80
|
-
var __dirname =
|
|
81
|
-
var packageJson = JSON.parse(readFileSync(
|
|
571
|
+
var __dirname = dirname2(__filename);
|
|
572
|
+
var packageJson = JSON.parse(readFileSync(join2(__dirname, "../package.json"), "utf-8"));
|
|
82
573
|
var program = new Command();
|
|
83
574
|
program.name("hono").description("CLI for Hono").version(packageJson.version, "-v, --version", "display version number");
|
|
84
|
-
createCommand(program);
|
|
85
575
|
docsCommand(program);
|
|
576
|
+
optimizeCommand(program);
|
|
577
|
+
searchCommand(program);
|
|
578
|
+
requestCommand(program);
|
|
579
|
+
serveCommand(program);
|
|
86
580
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hono/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"hono": "dist/cli.js"
|
|
@@ -33,13 +33,15 @@
|
|
|
33
33
|
},
|
|
34
34
|
"homepage": "https://hono.dev",
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"
|
|
36
|
+
"@hono/node-server": "^1.19.5",
|
|
37
|
+
"commander": "^14.0.1",
|
|
38
|
+
"esbuild": "^0.25.10",
|
|
39
|
+
"hono": "^4.9.12"
|
|
37
40
|
},
|
|
38
41
|
"devDependencies": {
|
|
39
42
|
"@hono/eslint-config": "^1.1.1",
|
|
40
43
|
"@types/node": "^24.7.0",
|
|
41
44
|
"eslint": "^9.37.0",
|
|
42
|
-
"hono": "4.4.13",
|
|
43
45
|
"np": "^10.2.0",
|
|
44
46
|
"prettier": "^3.6.2",
|
|
45
47
|
"publint": "^0.3.14",
|