@hono/cli 0.1.8 → 0.1.10

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 +13 -0
  2. package/dist/cli.js +229 -62
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -157,6 +157,12 @@ hono request [file] [options]
157
157
  - `-d, --data <data>` - Request body data
158
158
  - `-H, --header <header>` - Custom headers (can be used multiple times)
159
159
  - `-w, --watch` - Watch for changes and resend request
160
+ - `-J, --json` - Output response as JSON
161
+ - `-o, --output <file>` - Write to file instead of stdout
162
+ - `-O, --remote-name` - Write output to file named as remote file
163
+ - `-i, --include` - Include protocol and headers in the output
164
+ - `-I, --head` - Show only protocol and headers in the output
165
+ - `-e, --external <package>` - Mark package as external (can be used multiple times)
160
166
 
161
167
  **Examples:**
162
168
 
@@ -178,6 +184,9 @@ hono request -P /api/protected \
178
184
  -H 'Authorization: Bearer token' \
179
185
  -H 'User-Agent: MyApp' \
180
186
  src/your-app.ts
187
+
188
+ # Request with external packages (useful for Node.js native modules)
189
+ hono request -e pg -e dotenv src/your-app.ts
181
190
  ```
182
191
 
183
192
  **Response Format:**
@@ -212,6 +221,7 @@ hono serve [entry] [options]
212
221
  - `-p, --port <port>` - Port number (default: 7070)
213
222
  - `--show-routes` - Show registered routes
214
223
  - `--use <middleware>` - Use middleware (can be used multiple times)
224
+ - `-e, --external <package>` - Mark package as external (can be used multiple times)
215
225
 
216
226
  **Examples:**
217
227
 
@@ -238,6 +248,9 @@ hono serve --use 'cors()' --use 'logger()' src/app.ts
238
248
  hono serve \
239
249
  --use 'basicAuth({ username: "foo", password: "bar" })' \
240
250
  --use "serveStatic({ root: './' })"
251
+
252
+ # Start server with external packages (useful for Node.js native modules)
253
+ hono serve -e pg -e prisma src/app.ts
241
254
  ```
242
255
 
243
256
  ### `optimize`
package/dist/cli.js CHANGED
@@ -14665,7 +14665,7 @@ var require_lib = __commonJS({
14665
14665
  // src/cli.ts
14666
14666
  import { Command } from "commander";
14667
14667
  import { readFileSync as readFileSync2 } from "fs";
14668
- import { dirname as dirname2, join as join2 } from "path";
14668
+ import { dirname as dirname3, join as join2 } from "path";
14669
14669
  import { fileURLToPath } from "url";
14670
14670
 
14671
14671
  // src/commands/docs/index.ts
@@ -16206,43 +16206,179 @@ var isIdentifier = (key) => key.type === "Identifier";
16206
16206
  var isPrivateIdentifier = (key) => key.type === "PrivateName";
16207
16207
 
16208
16208
  // src/commands/request/index.ts
16209
- import { existsSync as existsSync2, realpathSync as realpathSync2 } from "fs";
16209
+ import { existsSync as existsSync3, realpathSync as realpathSync2 } from "fs";
16210
16210
  import { resolve as resolve2 } from "path";
16211
+
16212
+ // src/utils/file.ts
16213
+ import { getExtension } from "hono/utils/mime";
16214
+ import { existsSync as existsSync2, createWriteStream } from "fs";
16215
+ import { dirname as dirname2, basename } from "path";
16216
+ var getFilenameFromPath = (path, contentType) => {
16217
+ const url = new URL(path, "http://localhost");
16218
+ const pathname = url.pathname;
16219
+ if (pathname === "/") {
16220
+ if (contentType) {
16221
+ const parts = contentType.split(";");
16222
+ for (const part of parts) {
16223
+ const mimeType = part.trim().toLowerCase();
16224
+ const extension = getExtension(mimeType);
16225
+ if (extension) {
16226
+ return `index.${extension}`;
16227
+ }
16228
+ }
16229
+ }
16230
+ return "index";
16231
+ }
16232
+ const name = basename(pathname);
16233
+ return name;
16234
+ };
16235
+ var saveFile = async (buffer, filepath) => {
16236
+ if (existsSync2(filepath)) {
16237
+ throw new Error(`File ${filepath} already exists.`);
16238
+ }
16239
+ const dir = dirname2(filepath);
16240
+ if (!existsSync2(dir)) {
16241
+ throw new Error(`Directory ${dir} does not exist.`);
16242
+ }
16243
+ const totalBytes = buffer.byteLength;
16244
+ const view = new Uint8Array(buffer);
16245
+ const chunkSize = 1024 * 64;
16246
+ let savedBytes = 0;
16247
+ const stream = createWriteStream(filepath);
16248
+ return new Promise((resolve4, reject) => {
16249
+ stream.on("error", (err) => reject(err));
16250
+ const writeChunk = (index) => {
16251
+ if (index >= totalBytes) {
16252
+ stream.end(() => {
16253
+ resolve4();
16254
+ });
16255
+ return;
16256
+ }
16257
+ const end = Math.min(index + chunkSize, totalBytes);
16258
+ const chunk = view.slice(index, end);
16259
+ stream.write(chunk, (err) => {
16260
+ if (err) {
16261
+ stream.destroy(err);
16262
+ reject(err);
16263
+ return;
16264
+ }
16265
+ savedBytes += chunk.length;
16266
+ console.log(`Saved ${savedBytes} of ${totalBytes} bytes`);
16267
+ writeChunk(end);
16268
+ });
16269
+ };
16270
+ writeChunk(0);
16271
+ });
16272
+ };
16273
+
16274
+ // src/commands/request/index.ts
16211
16275
  var DEFAULT_ENTRY_CANDIDATES2 = ["src/index.ts", "src/index.tsx", "src/index.js", "src/index.jsx"];
16212
16276
  function requestCommand(program2) {
16213
- 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 resend request", false).option(
16277
+ 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 resend request", false).option("-J, --json", "Output response as JSON", false).option(
16214
16278
  "-H, --header <header>",
16215
16279
  "Custom headers",
16216
16280
  (value, previous) => {
16217
16281
  return previous ? [...previous, value] : [value];
16218
16282
  },
16219
16283
  []
16284
+ ).option("-o, --output <file>", "Write to file instead of stdout").option("-O, --remote-name", "Write output to file named as remote file", false).option("-i, --include", "Include protocol and headers in the output", false).option("-I, --head", "Show only protocol and headers in the output", false).option(
16285
+ "-e, --external <package>",
16286
+ "Mark package as external (can be used multiple times)",
16287
+ (value, previous) => {
16288
+ return previous ? [...previous, value] : [value];
16289
+ },
16290
+ []
16220
16291
  ).action(async (file, options) => {
16292
+ const doSaveFile = options.output || options.remoteName;
16221
16293
  const path = options.path || "/";
16222
16294
  const watch = options.watch;
16223
- const buildIterator = getBuildIterator(file, watch);
16295
+ const external = options.external || [];
16296
+ const buildIterator = getBuildIterator(file, watch, external);
16224
16297
  for await (const app of buildIterator) {
16225
16298
  const result = await executeRequest(app, path, options);
16226
- console.log(JSON.stringify(result, null, 2));
16299
+ const contentType = result.headers["content-type"];
16300
+ const outputBody = formatResponseBody(
16301
+ result.body,
16302
+ contentType,
16303
+ options.json && !options.include
16304
+ );
16305
+ const buffer = await result.response.clone().arrayBuffer();
16306
+ const isBinaryData = isBinaryResponse(buffer);
16307
+ if (isBinaryData && !doSaveFile) {
16308
+ console.warn("Binary output can mess up your terminal.");
16309
+ continue;
16310
+ }
16311
+ const outputData = getOutputData(
16312
+ buffer,
16313
+ outputBody,
16314
+ isBinaryData,
16315
+ options,
16316
+ result.status,
16317
+ result.headers
16318
+ );
16319
+ if (!isBinaryData) {
16320
+ console.log(outputData);
16321
+ }
16322
+ if (doSaveFile) {
16323
+ await handleSaveOutput(outputData, path, options, contentType);
16324
+ }
16227
16325
  }
16228
16326
  });
16229
16327
  }
16230
- function getBuildIterator(appPath, watch) {
16328
+ function getOutputData(buffer, outputBody, isBinaryData, options, status, headers) {
16329
+ if (isBinaryData) {
16330
+ return buffer;
16331
+ }
16332
+ const headerLines = [];
16333
+ headerLines.push(`${status}`);
16334
+ for (const key in headers) {
16335
+ headerLines.push(`\x1B[1m${key}\x1B[0m: ${headers[key]}`);
16336
+ }
16337
+ const headerOutput = headerLines.join("\n");
16338
+ if (options.head) {
16339
+ return headerOutput + "\n";
16340
+ }
16341
+ if (options.include) {
16342
+ return headerOutput + "\n\n" + outputBody;
16343
+ }
16344
+ if (options.json) {
16345
+ return JSON.stringify({ status, body: outputBody, headers }, null, 2);
16346
+ }
16347
+ return outputBody;
16348
+ }
16349
+ async function handleSaveOutput(saveData, requestPath, options, contentType) {
16350
+ let filepath;
16351
+ if (options.output) {
16352
+ filepath = options.output;
16353
+ } else {
16354
+ filepath = getFilenameFromPath(requestPath, contentType);
16355
+ }
16356
+ try {
16357
+ await saveFile(
16358
+ typeof saveData === "string" ? new TextEncoder().encode(saveData).buffer : saveData instanceof ArrayBuffer ? saveData : new TextEncoder().encode(JSON.stringify(saveData)).buffer,
16359
+ filepath
16360
+ );
16361
+ console.log(`Saved response to ${filepath}`);
16362
+ } catch (error) {
16363
+ console.error(`Error saving file: ${error.message}`);
16364
+ }
16365
+ }
16366
+ function getBuildIterator(appPath, watch, external = []) {
16231
16367
  let entry;
16232
16368
  let resolvedAppPath;
16233
16369
  if (appPath) {
16234
16370
  entry = appPath;
16235
16371
  resolvedAppPath = resolve2(process.cwd(), entry);
16236
16372
  } else {
16237
- entry = DEFAULT_ENTRY_CANDIDATES2.find((candidate) => existsSync2(resolve2(process.cwd(), candidate))) ?? DEFAULT_ENTRY_CANDIDATES2[0];
16373
+ entry = DEFAULT_ENTRY_CANDIDATES2.find((candidate) => existsSync3(resolve2(process.cwd(), candidate))) ?? DEFAULT_ENTRY_CANDIDATES2[0];
16238
16374
  resolvedAppPath = resolve2(process.cwd(), entry);
16239
16375
  }
16240
- if (!existsSync2(resolvedAppPath)) {
16376
+ if (!existsSync3(resolvedAppPath)) {
16241
16377
  throw new Error(`Entry file ${entry} does not exist`);
16242
16378
  }
16243
16379
  const appFilePath = realpathSync2(resolvedAppPath);
16244
16380
  return buildAndImportApp(appFilePath, {
16245
- external: ["@hono/node-server"],
16381
+ external: ["@hono/node-server", ...external],
16246
16382
  watch,
16247
16383
  sourcemap: true
16248
16384
  });
@@ -16271,13 +16407,39 @@ async function executeRequest(app, requestPath, options) {
16271
16407
  response.headers.forEach((value, key) => {
16272
16408
  responseHeaders[key] = value;
16273
16409
  });
16274
- const body = await response.text();
16410
+ const body = await response.clone().text();
16275
16411
  return {
16276
16412
  status: response.status,
16277
16413
  body,
16278
- headers: responseHeaders
16414
+ headers: responseHeaders,
16415
+ response
16279
16416
  };
16280
16417
  }
16418
+ var formatResponseBody = (responseBody, contentType, jsonOption) => {
16419
+ if (contentType && /^application\/(json|[^;\s]+\+json)($|;)/i.test(contentType)) {
16420
+ try {
16421
+ const parsedJSON = JSON.parse(responseBody);
16422
+ if (jsonOption) {
16423
+ return parsedJSON;
16424
+ }
16425
+ return JSON.stringify(parsedJSON, null, 2);
16426
+ } catch {
16427
+ console.error("Response indicated JSON content type but failed to parse JSON.");
16428
+ return responseBody;
16429
+ }
16430
+ }
16431
+ return responseBody;
16432
+ };
16433
+ var isBinaryResponse = (buffer) => {
16434
+ const view = new Uint8Array(buffer);
16435
+ const len = Math.min(view.length, 2e3);
16436
+ for (let i = 0; i < len; i++) {
16437
+ if (view[i] === 0) {
16438
+ return true;
16439
+ }
16440
+ }
16441
+ return false;
16442
+ };
16281
16443
 
16282
16444
  // src/commands/search/index.ts
16283
16445
  function searchCommand(program2) {
@@ -16403,7 +16565,7 @@ import { serve } from "@hono/node-server";
16403
16565
  import { serveStatic } from "@hono/node-server/serve-static";
16404
16566
  import { Hono } from "hono";
16405
16567
  import { showRoutes } from "hono/dev";
16406
- import { existsSync as existsSync3, realpathSync as realpathSync3 } from "fs";
16568
+ import { existsSync as existsSync4, realpathSync as realpathSync3 } from "fs";
16407
16569
  import { resolve as resolve3 } from "path";
16408
16570
 
16409
16571
  // src/commands/serve/builtin-map.ts
@@ -16496,67 +16658,72 @@ function serveCommand(program2) {
16496
16658
  return previous ? [...previous, value] : [value];
16497
16659
  },
16498
16660
  []
16499
- ).action(
16500
- async (entry, options) => {
16501
- let app;
16502
- if (!entry) {
16661
+ ).option(
16662
+ "-e, --external <package>",
16663
+ "Mark package as external (can be used multiple times)",
16664
+ (value, previous) => {
16665
+ return previous ? [...previous, value] : [value];
16666
+ },
16667
+ []
16668
+ ).action(async (entry, options) => {
16669
+ let app;
16670
+ if (!entry) {
16671
+ app = new Hono();
16672
+ } else {
16673
+ const appPath = resolve3(process.cwd(), entry);
16674
+ if (!existsSync4(appPath)) {
16503
16675
  app = new Hono();
16504
16676
  } else {
16505
- const appPath = resolve3(process.cwd(), entry);
16506
- if (!existsSync3(appPath)) {
16507
- app = new Hono();
16508
- } else {
16509
- const appFilePath = realpathSync3(appPath);
16510
- const buildIterator = buildAndImportApp(appFilePath, {
16511
- external: ["@hono/node-server"],
16512
- sourcemap: true
16513
- });
16514
- app = (await buildIterator.next()).value;
16515
- }
16677
+ const appFilePath = realpathSync3(appPath);
16678
+ const buildIterator = buildAndImportApp(appFilePath, {
16679
+ external: ["@hono/node-server", ...options.external || []],
16680
+ sourcemap: true
16681
+ });
16682
+ app = (await buildIterator.next()).value;
16516
16683
  }
16517
- const allFunctions = {};
16518
- const uniqueModules = [...new Set(Object.values(builtinMap))];
16519
- for (const modulePath of uniqueModules) {
16520
- try {
16521
- const module = await import(modulePath);
16522
- for (const [funcName, modulePathInMap] of Object.entries(builtinMap)) {
16523
- if (modulePathInMap === modulePath && module[funcName]) {
16524
- allFunctions[funcName] = module[funcName];
16525
- }
16684
+ }
16685
+ const allFunctions = {};
16686
+ const uniqueModules = [...new Set(Object.values(builtinMap))];
16687
+ for (const modulePath of uniqueModules) {
16688
+ try {
16689
+ const module = await import(modulePath);
16690
+ for (const [funcName, modulePathInMap] of Object.entries(builtinMap)) {
16691
+ if (modulePathInMap === modulePath && module[funcName]) {
16692
+ allFunctions[funcName] = module[funcName];
16526
16693
  }
16527
- } catch (error) {
16528
16694
  }
16695
+ } catch (error) {
16529
16696
  }
16530
- const baseApp = new Hono();
16531
- for (const use of options.use || []) {
16532
- const functionNames = Object.keys(allFunctions);
16533
- const functionValues = Object.values(allFunctions);
16534
- const func = new Function("c", "next", ...functionNames, `return (${use})`);
16535
- baseApp.use(async (c, next) => {
16536
- const middleware = func(c, next, ...functionValues);
16537
- return typeof middleware === "function" ? middleware(c, next) : middleware;
16538
- });
16539
- }
16540
- baseApp.route("/", app);
16541
- if (options.showRoutes) {
16542
- showRoutes(baseApp);
16543
- }
16544
- serve(
16545
- {
16546
- fetch: baseApp.fetch,
16547
- port: options.port ?? 7070
16548
- },
16549
- (info) => {
16550
- console.log(`Listening on http://localhost:${info.port}`);
16551
- }
16552
- );
16553
16697
  }
16554
- );
16698
+ const baseApp = new Hono();
16699
+ for (const use of options.use || []) {
16700
+ const functionNames = Object.keys(allFunctions);
16701
+ const functionValues = Object.values(allFunctions);
16702
+ const func = new Function("c", "next", ...functionNames, `return (${use})`);
16703
+ baseApp.use(async (c, next) => {
16704
+ const middleware = func(c, next, ...functionValues);
16705
+ return typeof middleware === "function" ? middleware(c, next) : middleware;
16706
+ });
16707
+ }
16708
+ baseApp.route("/", app);
16709
+ if (options.showRoutes) {
16710
+ showRoutes(baseApp);
16711
+ }
16712
+ serve(
16713
+ {
16714
+ fetch: baseApp.fetch,
16715
+ port: options.port ?? 7070
16716
+ },
16717
+ (info) => {
16718
+ console.log(`Listening on http://localhost:${info.port}`);
16719
+ }
16720
+ );
16721
+ });
16555
16722
  }
16556
16723
 
16557
16724
  // src/cli.ts
16558
16725
  var __filename = fileURLToPath(import.meta.url);
16559
- var __dirname = dirname2(__filename);
16726
+ var __dirname = dirname3(__filename);
16560
16727
  var packageJson = JSON.parse(readFileSync2(join2(__dirname, "../package.json"), "utf-8"));
16561
16728
  var program = new Command();
16562
16729
  program.name("hono").description("CLI for Hono").version(packageJson.version, "-v, --version", "display version number");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hono/cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "hono": "dist/cli.js"