@hono/cli 0.1.3 → 0.1.5

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 (2) hide show
  1. package/dist/cli.js +162 -115
  2. package/package.json +2 -1
package/dist/cli.js CHANGED
@@ -70,114 +70,154 @@ 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
+ sourcemap: options.sourcemap ?? false,
85
+ sourcesContent: false,
86
+ sourceRoot: process.cwd(),
87
+ bundle: true,
88
+ write: false,
89
+ format: "esm",
90
+ target: "node20",
91
+ jsx: "automatic",
92
+ jsxImportSource: "hono/jsx",
93
+ platform: "node",
94
+ external: options.external || [],
95
+ plugins: [
96
+ {
97
+ name: "watch",
98
+ setup(build2) {
99
+ build2.onEnd(async (result) => {
100
+ try {
101
+ let code = result.outputFiles?.[0]?.text || "";
102
+ if (options.sourcemap) {
103
+ code += `
104
+ //# sourceURL=file://${process.cwd()}/__hono_cli_bundle__.js`;
105
+ }
106
+ const dataUrl = `data:text/javascript;base64,${Buffer.from(code).toString("base64")}`;
107
+ const module = await import(dataUrl);
108
+ const app = module.default;
109
+ if (!app) {
110
+ throw new Error("Failed to build app");
111
+ }
112
+ if (!app || typeof app.request !== "function") {
113
+ throw new Error("No valid Hono app exported from the file");
114
+ }
115
+ try {
116
+ resolveApp(app);
117
+ } catch {
118
+ }
119
+ } catch (error) {
120
+ console.error("Error building app", error);
121
+ }
122
+ });
123
+ }
124
+ }
125
+ ]
126
+ });
127
+ await context2.watch();
128
+ if (!options.watch) {
129
+ await context2.dispose();
96
130
  }
131
+ do {
132
+ yield await appPromise;
133
+ preparePromise();
134
+ } while (options.watch);
97
135
  }
98
136
 
99
137
  // src/commands/optimize/index.ts
100
138
  var DEFAULT_ENTRY_CANDIDATES = ["src/index.ts", "src/index.tsx", "src/index.js", "src/index.jsx"];
101
139
  function optimizeCommand(program2) {
102
- 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) => {
103
- if (!entry) {
104
- entry = DEFAULT_ENTRY_CANDIDATES.find((entry2) => existsSync(entry2)) ?? DEFAULT_ENTRY_CANDIDATES[0];
105
- }
106
- const appPath = resolve(process.cwd(), entry);
107
- if (!existsSync(appPath)) {
108
- throw new Error(`Entry file ${entry} does not exist`);
109
- }
110
- const appFilePath = realpathSync(appPath);
111
- const app = await buildAndImportApp(appFilePath, {
112
- external: ["@hono/node-server"]
113
- });
114
- let routerName;
115
- let importStatement;
116
- let assignRouterStatement;
117
- try {
118
- const serialized = serializeInitParams(
119
- buildInitParams({
120
- paths: app.routes.map(({ path }) => path)
121
- })
122
- );
123
- const hasPreparedRegExpRouter = await new Promise((resolve4) => {
124
- const child = execFile(process.execPath, [
125
- "--input-type=module",
126
- "-e",
127
- "try { (await import('hono/router/reg-exp-router')).PreparedRegExpRouter && process.exit(0) } finally { process.exit(1) }"
128
- ]);
129
- child.on("exit", (code) => {
130
- resolve4(code === 0);
131
- });
140
+ 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").option("-t, --target [target]", "environment target (e.g., node24, deno2, es2024)", "node20").action(
141
+ async (entry, options) => {
142
+ if (!entry) {
143
+ entry = DEFAULT_ENTRY_CANDIDATES.find((entry2) => existsSync(entry2)) ?? DEFAULT_ENTRY_CANDIDATES[0];
144
+ }
145
+ const appPath = resolve(process.cwd(), entry);
146
+ if (!existsSync(appPath)) {
147
+ throw new Error(`Entry file ${entry} does not exist`);
148
+ }
149
+ const appFilePath = realpathSync(appPath);
150
+ const buildIterator = buildAndImportApp(appFilePath, {
151
+ external: ["@hono/node-server"]
132
152
  });
133
- if (hasPreparedRegExpRouter) {
134
- routerName = "PreparedRegExpRouter";
135
- importStatement = "import { PreparedRegExpRouter } from 'hono/router/reg-exp-router'";
136
- assignRouterStatement = `const routerParams = ${serialized}
153
+ const app = (await buildIterator.next()).value;
154
+ let routerName;
155
+ let importStatement;
156
+ let assignRouterStatement;
157
+ try {
158
+ const serialized = serializeInitParams(
159
+ buildInitParams({
160
+ paths: app.routes.map(({ path }) => path)
161
+ })
162
+ );
163
+ const hasPreparedRegExpRouter = await new Promise((resolve4) => {
164
+ const child = execFile(process.execPath, [
165
+ "--input-type=module",
166
+ "-e",
167
+ "try { (await import('hono/router/reg-exp-router')).PreparedRegExpRouter && process.exit(0) } finally { process.exit(1) }"
168
+ ]);
169
+ child.on("exit", (code) => {
170
+ resolve4(code === 0);
171
+ });
172
+ });
173
+ if (hasPreparedRegExpRouter) {
174
+ routerName = "PreparedRegExpRouter";
175
+ importStatement = "import { PreparedRegExpRouter } from 'hono/router/reg-exp-router'";
176
+ assignRouterStatement = `const routerParams = ${serialized}
137
177
  this.router = new PreparedRegExpRouter(...routerParams)`;
138
- } else {
139
- routerName = "RegExpRouter";
140
- importStatement = "import { RegExpRouter } from 'hono/router/reg-exp-router'";
141
- assignRouterStatement = "this.router = new RegExpRouter()";
178
+ } else {
179
+ routerName = "RegExpRouter";
180
+ importStatement = "import { RegExpRouter } from 'hono/router/reg-exp-router'";
181
+ assignRouterStatement = "this.router = new RegExpRouter()";
182
+ }
183
+ } catch {
184
+ routerName = "TrieRouter";
185
+ importStatement = "import { TrieRouter } from 'hono/router/trie-router'";
186
+ assignRouterStatement = "this.router = new TrieRouter()";
142
187
  }
143
- } catch {
144
- routerName = "TrieRouter";
145
- importStatement = "import { TrieRouter } from 'hono/router/trie-router'";
146
- assignRouterStatement = "this.router = new TrieRouter()";
147
- }
148
- console.log("[Optimized]");
149
- console.log(` Router: ${routerName}`);
150
- const outfile = resolve(process.cwd(), options.outfile);
151
- await esbuild2.build({
152
- entryPoints: [appFilePath],
153
- outfile,
154
- bundle: true,
155
- minify: options.minify,
156
- format: "esm",
157
- target: "node20",
158
- platform: "node",
159
- jsx: "automatic",
160
- jsxImportSource: "hono/jsx",
161
- plugins: [
162
- {
163
- name: "hono-optimize",
164
- setup(build3) {
165
- const honoPseudoImportPath = "hono-optimized-pseudo-import-path";
166
- build3.onResolve({ filter: /^hono$/ }, async (args) => {
167
- if (!args.importer) {
168
- return void 0;
169
- }
170
- const resolved = await build3.resolve(args.path, {
171
- kind: "import-statement",
172
- resolveDir: args.resolveDir
188
+ console.log("[Optimized]");
189
+ console.log(` Router: ${routerName}`);
190
+ const outfile = resolve(process.cwd(), options.outfile);
191
+ await esbuild2.build({
192
+ entryPoints: [appFilePath],
193
+ outfile,
194
+ bundle: true,
195
+ minify: options.minify,
196
+ format: "esm",
197
+ target: options.target,
198
+ platform: "node",
199
+ jsx: "automatic",
200
+ jsxImportSource: "hono/jsx",
201
+ plugins: [
202
+ {
203
+ name: "hono-optimize",
204
+ setup(build2) {
205
+ const honoPseudoImportPath = "hono-optimized-pseudo-import-path";
206
+ build2.onResolve({ filter: /^hono$/ }, async (args) => {
207
+ if (!args.importer) {
208
+ return void 0;
209
+ }
210
+ const resolved = await build2.resolve(args.path, {
211
+ kind: "import-statement",
212
+ resolveDir: args.resolveDir
213
+ });
214
+ return {
215
+ path: join(dirname(resolved.path), honoPseudoImportPath)
216
+ };
173
217
  });
174
- return {
175
- path: join(dirname(resolved.path), honoPseudoImportPath)
176
- };
177
- });
178
- build3.onLoad({ filter: new RegExp(`/${honoPseudoImportPath}$`) }, async () => {
179
- return {
180
- contents: `
218
+ build2.onLoad({ filter: new RegExp(`/${honoPseudoImportPath}$`) }, async () => {
219
+ return {
220
+ contents: `
181
221
  import { HonoBase } from 'hono/hono-base'
182
222
  ${importStatement}
183
223
  export class Hono extends HonoBase {
@@ -187,15 +227,16 @@ export class Hono extends HonoBase {
187
227
  }
188
228
  }
189
229
  `
190
- };
191
- });
230
+ };
231
+ });
232
+ }
192
233
  }
193
- }
194
- ]
195
- });
196
- const outfileStat = statSync(outfile);
197
- console.log(` Output: ${options.outfile} (${(outfileStat.size / 1024).toFixed(2)} KB)`);
198
- });
234
+ ]
235
+ });
236
+ const outfileStat = statSync(outfile);
237
+ console.log(` Output: ${options.outfile} (${(outfileStat.size / 1024).toFixed(2)} KB)`);
238
+ }
239
+ );
199
240
  }
200
241
 
201
242
  // src/commands/request/index.ts
@@ -203,7 +244,7 @@ import { existsSync as existsSync2, realpathSync as realpathSync2 } from "fs";
203
244
  import { resolve as resolve2 } from "path";
204
245
  var DEFAULT_ENTRY_CANDIDATES2 = ["src/index.ts", "src/index.tsx", "src/index.js", "src/index.jsx"];
205
246
  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(
247
+ 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(
207
248
  "-H, --header <header>",
208
249
  "Custom headers",
209
250
  (value, previous) => {
@@ -212,11 +253,15 @@ function requestCommand(program2) {
212
253
  []
213
254
  ).action(async (file, options) => {
214
255
  const path = options.path || "/";
215
- const result = await executeRequest(file, path, options);
216
- console.log(JSON.stringify(result, null, 2));
256
+ const watch = options.watch;
257
+ const buildIterator = getBuildIterator(file, watch);
258
+ for await (const app of buildIterator) {
259
+ const result = await executeRequest(app, path, options);
260
+ console.log(JSON.stringify(result, null, 2));
261
+ }
217
262
  });
218
263
  }
219
- async function executeRequest(appPath, requestPath, options) {
264
+ function getBuildIterator(appPath, watch) {
220
265
  let entry;
221
266
  let resolvedAppPath;
222
267
  if (appPath) {
@@ -230,12 +275,13 @@ async function executeRequest(appPath, requestPath, options) {
230
275
  throw new Error(`Entry file ${entry} does not exist`);
231
276
  }
232
277
  const appFilePath = realpathSync2(resolvedAppPath);
233
- const app = await buildAndImportApp(appFilePath, {
234
- external: ["@hono/node-server"]
278
+ return buildAndImportApp(appFilePath, {
279
+ external: ["@hono/node-server"],
280
+ watch,
281
+ sourcemap: true
235
282
  });
236
- if (!app || typeof app.request !== "function") {
237
- throw new Error("No valid Hono app exported from the file");
238
- }
283
+ }
284
+ async function executeRequest(app, requestPath, options) {
239
285
  const url = new URL(requestPath, "http://localhost");
240
286
  const requestInit = {
241
287
  method: options.method || "GET"
@@ -495,9 +541,10 @@ function serveCommand(program2) {
495
541
  app = new Hono();
496
542
  } else {
497
543
  const appFilePath = realpathSync3(appPath);
498
- app = await buildAndImportApp(appFilePath, {
544
+ const buildIterator = buildAndImportApp(appFilePath, {
499
545
  external: ["@hono/node-server"]
500
546
  });
547
+ app = (await buildIterator.next()).value;
501
548
  }
502
549
  }
503
550
  const allFunctions = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hono/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "hono": "dist/cli.js"
@@ -43,6 +43,7 @@
43
43
  "@types/node": "^24.7.0",
44
44
  "eslint": "^9.37.0",
45
45
  "np": "^10.2.0",
46
+ "pkg-pr-new": "^0.0.62",
46
47
  "prettier": "^3.6.2",
47
48
  "publint": "^0.3.14",
48
49
  "tsup": "^8.5.0",