@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.
- package/README.md +13 -0
- package/dist/cli.js +229 -62
- 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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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) =>
|
|
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 (!
|
|
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
|
|
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
|
-
).
|
|
16500
|
-
|
|
16501
|
-
|
|
16502
|
-
|
|
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
|
|
16506
|
-
|
|
16507
|
-
|
|
16508
|
-
|
|
16509
|
-
|
|
16510
|
-
|
|
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
|
-
|
|
16518
|
-
|
|
16519
|
-
|
|
16520
|
-
|
|
16521
|
-
|
|
16522
|
-
|
|
16523
|
-
|
|
16524
|
-
|
|
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 =
|
|
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");
|