@hono/cli 0.1.9 → 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 +5 -0
  2. package/dist/cli.js +166 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -157,6 +157,11 @@ 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
160
165
  - `-e, --external <package>` - Mark package as external (can be used multiple times)
161
166
 
162
167
  **Examples:**
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,18 +16206,82 @@ 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
  []
16220
- ).option(
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(
16221
16285
  "-e, --external <package>",
16222
16286
  "Mark package as external (can be used multiple times)",
16223
16287
  (value, previous) => {
@@ -16225,16 +16289,80 @@ function requestCommand(program2) {
16225
16289
  },
16226
16290
  []
16227
16291
  ).action(async (file, options) => {
16292
+ const doSaveFile = options.output || options.remoteName;
16228
16293
  const path = options.path || "/";
16229
16294
  const watch = options.watch;
16230
16295
  const external = options.external || [];
16231
16296
  const buildIterator = getBuildIterator(file, watch, external);
16232
16297
  for await (const app of buildIterator) {
16233
16298
  const result = await executeRequest(app, path, options);
16234
- 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
+ }
16235
16325
  }
16236
16326
  });
16237
16327
  }
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
+ }
16238
16366
  function getBuildIterator(appPath, watch, external = []) {
16239
16367
  let entry;
16240
16368
  let resolvedAppPath;
@@ -16242,10 +16370,10 @@ function getBuildIterator(appPath, watch, external = []) {
16242
16370
  entry = appPath;
16243
16371
  resolvedAppPath = resolve2(process.cwd(), entry);
16244
16372
  } else {
16245
- 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];
16246
16374
  resolvedAppPath = resolve2(process.cwd(), entry);
16247
16375
  }
16248
- if (!existsSync2(resolvedAppPath)) {
16376
+ if (!existsSync3(resolvedAppPath)) {
16249
16377
  throw new Error(`Entry file ${entry} does not exist`);
16250
16378
  }
16251
16379
  const appFilePath = realpathSync2(resolvedAppPath);
@@ -16279,13 +16407,39 @@ async function executeRequest(app, requestPath, options) {
16279
16407
  response.headers.forEach((value, key) => {
16280
16408
  responseHeaders[key] = value;
16281
16409
  });
16282
- const body = await response.text();
16410
+ const body = await response.clone().text();
16283
16411
  return {
16284
16412
  status: response.status,
16285
16413
  body,
16286
- headers: responseHeaders
16414
+ headers: responseHeaders,
16415
+ response
16287
16416
  };
16288
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
+ };
16289
16443
 
16290
16444
  // src/commands/search/index.ts
16291
16445
  function searchCommand(program2) {
@@ -16411,7 +16565,7 @@ import { serve } from "@hono/node-server";
16411
16565
  import { serveStatic } from "@hono/node-server/serve-static";
16412
16566
  import { Hono } from "hono";
16413
16567
  import { showRoutes } from "hono/dev";
16414
- import { existsSync as existsSync3, realpathSync as realpathSync3 } from "fs";
16568
+ import { existsSync as existsSync4, realpathSync as realpathSync3 } from "fs";
16415
16569
  import { resolve as resolve3 } from "path";
16416
16570
 
16417
16571
  // src/commands/serve/builtin-map.ts
@@ -16517,7 +16671,7 @@ function serveCommand(program2) {
16517
16671
  app = new Hono();
16518
16672
  } else {
16519
16673
  const appPath = resolve3(process.cwd(), entry);
16520
- if (!existsSync3(appPath)) {
16674
+ if (!existsSync4(appPath)) {
16521
16675
  app = new Hono();
16522
16676
  } else {
16523
16677
  const appFilePath = realpathSync3(appPath);
@@ -16569,7 +16723,7 @@ function serveCommand(program2) {
16569
16723
 
16570
16724
  // src/cli.ts
16571
16725
  var __filename = fileURLToPath(import.meta.url);
16572
- var __dirname = dirname2(__filename);
16726
+ var __dirname = dirname3(__filename);
16573
16727
  var packageJson = JSON.parse(readFileSync2(join2(__dirname, "../package.json"), "utf-8"));
16574
16728
  var program = new Command();
16575
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.9",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "hono": "dist/cli.js"