@hono/cli 0.1.0 → 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.
Files changed (3) hide show
  1. package/README.md +1 -0
  2. package/dist/cli.js +83 -40
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -156,6 +156,7 @@ hono request [file] [options]
156
156
  - `-X, --method <method>` - HTTP method (default: GET)
157
157
  - `-d, --data <data>` - Request body data
158
158
  - `-H, --header <header>` - Custom headers (can be used multiple times)
159
+ - `-w, --watch` - Watch for changes and resend request
159
160
 
160
161
  **Examples:**
161
162
 
package/dist/cli.js CHANGED
@@ -70,30 +70,61 @@ import { dirname, join, resolve } from "path";
70
70
 
71
71
  // src/utils/build.ts
72
72
  import * as esbuild from "esbuild";
73
- import { extname } from "path";
74
- import { pathToFileURL } from "url";
75
- async function buildAndImportApp(filePath, options = {}) {
76
- const ext = extname(filePath);
77
- if ([".ts", ".tsx", ".jsx"].includes(ext)) {
78
- const result = await esbuild.build({
79
- entryPoints: [filePath],
80
- bundle: true,
81
- write: false,
82
- format: "esm",
83
- target: "node20",
84
- jsx: "automatic",
85
- jsxImportSource: "hono/jsx",
86
- platform: "node",
87
- external: options.external || []
73
+ async function* buildAndImportApp(filePath, options = {}) {
74
+ let resolveApp;
75
+ let appPromise;
76
+ const preparePromise = () => {
77
+ appPromise = new Promise((resolve4) => {
78
+ resolveApp = resolve4;
88
79
  });
89
- const code = result.outputFiles[0].text;
90
- const dataUrl = `data:text/javascript;base64,${Buffer.from(code).toString("base64")}`;
91
- const module = await import(dataUrl);
92
- return module.default;
93
- } else {
94
- const module = await import(pathToFileURL(filePath).href);
95
- return module.default;
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
+ }
117
+ }
118
+ ]
119
+ });
120
+ await context2.watch();
121
+ if (!options.watch) {
122
+ await context2.dispose();
96
123
  }
124
+ do {
125
+ yield await appPromise;
126
+ preparePromise();
127
+ } while (options.watch);
97
128
  }
98
129
 
99
130
  // src/commands/optimize/index.ts
@@ -108,9 +139,10 @@ function optimizeCommand(program2) {
108
139
  throw new Error(`Entry file ${entry} does not exist`);
109
140
  }
110
141
  const appFilePath = realpathSync(appPath);
111
- const app = await buildAndImportApp(appFilePath, {
142
+ const buildIterator = buildAndImportApp(appFilePath, {
112
143
  external: ["@hono/node-server"]
113
144
  });
145
+ const app = (await buildIterator.next()).value;
114
146
  let routerName;
115
147
  let importStatement;
116
148
  let assignRouterStatement;
@@ -161,13 +193,13 @@ function optimizeCommand(program2) {
161
193
  plugins: [
162
194
  {
163
195
  name: "hono-optimize",
164
- setup(build3) {
196
+ setup(build2) {
165
197
  const honoPseudoImportPath = "hono-optimized-pseudo-import-path";
166
- build3.onResolve({ filter: /^hono$/ }, async (args) => {
198
+ build2.onResolve({ filter: /^hono$/ }, async (args) => {
167
199
  if (!args.importer) {
168
200
  return void 0;
169
201
  }
170
- const resolved = await build3.resolve(args.path, {
202
+ const resolved = await build2.resolve(args.path, {
171
203
  kind: "import-statement",
172
204
  resolveDir: args.resolveDir
173
205
  });
@@ -175,7 +207,7 @@ function optimizeCommand(program2) {
175
207
  path: join(dirname(resolved.path), honoPseudoImportPath)
176
208
  };
177
209
  });
178
- build3.onLoad({ filter: new RegExp(`/${honoPseudoImportPath}$`) }, async () => {
210
+ build2.onLoad({ filter: new RegExp(`/${honoPseudoImportPath}$`) }, async () => {
179
211
  return {
180
212
  contents: `
181
213
  import { HonoBase } from 'hono/hono-base'
@@ -203,7 +235,7 @@ import { existsSync as existsSync2, realpathSync as realpathSync2 } from "fs";
203
235
  import { resolve as resolve2 } from "path";
204
236
  var DEFAULT_ENTRY_CANDIDATES2 = ["src/index.ts", "src/index.tsx", "src/index.js", "src/index.jsx"];
205
237
  function requestCommand(program2) {
206
- 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(
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(
207
239
  "-H, --header <header>",
208
240
  "Custom headers",
209
241
  (value, previous) => {
@@ -212,11 +244,15 @@ function requestCommand(program2) {
212
244
  []
213
245
  ).action(async (file, options) => {
214
246
  const path = options.path || "/";
215
- const result = await executeRequest(file, path, options);
216
- console.log(JSON.stringify(result, null, 2));
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
+ }
217
253
  });
218
254
  }
219
- async function executeRequest(appPath, requestPath, options) {
255
+ function getBuildIterator(appPath, watch) {
220
256
  let entry;
221
257
  let resolvedAppPath;
222
258
  if (appPath) {
@@ -230,12 +266,12 @@ async function executeRequest(appPath, requestPath, options) {
230
266
  throw new Error(`Entry file ${entry} does not exist`);
231
267
  }
232
268
  const appFilePath = realpathSync2(resolvedAppPath);
233
- const app = await buildAndImportApp(appFilePath, {
234
- external: ["@hono/node-server"]
269
+ return buildAndImportApp(appFilePath, {
270
+ external: ["@hono/node-server"],
271
+ watch
235
272
  });
236
- if (!app || typeof app.request !== "function") {
237
- throw new Error("No valid Hono app exported from the file");
238
- }
273
+ }
274
+ async function executeRequest(app, requestPath, options) {
239
275
  const url = new URL(requestPath, "http://localhost");
240
276
  const requestInit = {
241
277
  method: options.method || "GET"
@@ -269,11 +305,17 @@ async function executeRequest(appPath, requestPath, options) {
269
305
 
270
306
  // src/commands/search/index.ts
271
307
  function searchCommand(program2) {
272
- program2.command("search").argument("<query>", "Search query for Hono documentation").option("-l, --limit <number>", "Number of results to show (default: 5)", "5").option("-p, --pretty", "Display results in human-readable format").description("Search Hono documentation").action(async (query, options) => {
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) => {
273
316
  const ALGOLIA_APP_ID = "1GIFSU1REV";
274
317
  const ALGOLIA_API_KEY = "c6a0f86b9a9f8551654600f28317a9e9";
275
318
  const ALGOLIA_INDEX = "hono";
276
- const limit = Math.max(1, Math.min(20, parseInt(options.limit, 10) || 5));
277
319
  const searchUrl = `https://${ALGOLIA_APP_ID}-dsn.algolia.net/1/indexes/${ALGOLIA_INDEX}/query`;
278
320
  try {
279
321
  if (options.pretty) {
@@ -288,7 +330,7 @@ function searchCommand(program2) {
288
330
  },
289
331
  body: JSON.stringify({
290
332
  query,
291
- hitsPerPage: limit
333
+ hitsPerPage: options.limit || 5
292
334
  })
293
335
  });
294
336
  if (!response.ok) {
@@ -478,9 +520,10 @@ function serveCommand(program2) {
478
520
  app = new Hono();
479
521
  } else {
480
522
  const appFilePath = realpathSync3(appPath);
481
- app = await buildAndImportApp(appFilePath, {
523
+ const buildIterator = buildAndImportApp(appFilePath, {
482
524
  external: ["@hono/node-server"]
483
525
  });
526
+ app = (await buildIterator.next()).value;
484
527
  }
485
528
  }
486
529
  const allFunctions = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hono/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "hono": "dist/cli.js"