@hono/cli 0.1.2 → 0.1.4
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 +5 -0
- package/dist/cli.js +154 -115
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -255,6 +255,8 @@ hono optimize [entry] [options]
|
|
|
255
255
|
**Options:**
|
|
256
256
|
|
|
257
257
|
- `-o, --outfile <outfile>` - Output file
|
|
258
|
+
- `-m, --minify` - minify output file
|
|
259
|
+
- `-t, --target [target]` - environment target
|
|
258
260
|
|
|
259
261
|
**Examples:**
|
|
260
262
|
|
|
@@ -267,6 +269,9 @@ hono optimize -o dist/app.js src/app.ts
|
|
|
267
269
|
|
|
268
270
|
# Export bundled file with minification
|
|
269
271
|
hono optimize -m
|
|
272
|
+
|
|
273
|
+
# Specify environment target
|
|
274
|
+
hono optimize -t es2024
|
|
270
275
|
```
|
|
271
276
|
|
|
272
277
|
## Tips
|
package/dist/cli.js
CHANGED
|
@@ -70,114 +70,147 @@ import { dirname, join, resolve } from "path";
|
|
|
70
70
|
|
|
71
71
|
// src/utils/build.ts
|
|
72
72
|
import * as esbuild from "esbuild";
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|
100
131
|
var DEFAULT_ENTRY_CANDIDATES = ["src/index.ts", "src/index.tsx", "src/index.js", "src/index.jsx"];
|
|
101
132
|
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").
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
});
|
|
133
|
+
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(
|
|
134
|
+
async (entry, options) => {
|
|
135
|
+
if (!entry) {
|
|
136
|
+
entry = DEFAULT_ENTRY_CANDIDATES.find((entry2) => existsSync(entry2)) ?? DEFAULT_ENTRY_CANDIDATES[0];
|
|
137
|
+
}
|
|
138
|
+
const appPath = resolve(process.cwd(), entry);
|
|
139
|
+
if (!existsSync(appPath)) {
|
|
140
|
+
throw new Error(`Entry file ${entry} does not exist`);
|
|
141
|
+
}
|
|
142
|
+
const appFilePath = realpathSync(appPath);
|
|
143
|
+
const buildIterator = buildAndImportApp(appFilePath, {
|
|
144
|
+
external: ["@hono/node-server"]
|
|
132
145
|
});
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
const app = (await buildIterator.next()).value;
|
|
147
|
+
let routerName;
|
|
148
|
+
let importStatement;
|
|
149
|
+
let assignRouterStatement;
|
|
150
|
+
try {
|
|
151
|
+
const serialized = serializeInitParams(
|
|
152
|
+
buildInitParams({
|
|
153
|
+
paths: app.routes.map(({ path }) => path)
|
|
154
|
+
})
|
|
155
|
+
);
|
|
156
|
+
const hasPreparedRegExpRouter = await new Promise((resolve4) => {
|
|
157
|
+
const child = execFile(process.execPath, [
|
|
158
|
+
"--input-type=module",
|
|
159
|
+
"-e",
|
|
160
|
+
"try { (await import('hono/router/reg-exp-router')).PreparedRegExpRouter && process.exit(0) } finally { process.exit(1) }"
|
|
161
|
+
]);
|
|
162
|
+
child.on("exit", (code) => {
|
|
163
|
+
resolve4(code === 0);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
if (hasPreparedRegExpRouter) {
|
|
167
|
+
routerName = "PreparedRegExpRouter";
|
|
168
|
+
importStatement = "import { PreparedRegExpRouter } from 'hono/router/reg-exp-router'";
|
|
169
|
+
assignRouterStatement = `const routerParams = ${serialized}
|
|
137
170
|
this.router = new PreparedRegExpRouter(...routerParams)`;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
171
|
+
} else {
|
|
172
|
+
routerName = "RegExpRouter";
|
|
173
|
+
importStatement = "import { RegExpRouter } from 'hono/router/reg-exp-router'";
|
|
174
|
+
assignRouterStatement = "this.router = new RegExpRouter()";
|
|
175
|
+
}
|
|
176
|
+
} catch {
|
|
177
|
+
routerName = "TrieRouter";
|
|
178
|
+
importStatement = "import { TrieRouter } from 'hono/router/trie-router'";
|
|
179
|
+
assignRouterStatement = "this.router = new TrieRouter()";
|
|
142
180
|
}
|
|
143
|
-
|
|
144
|
-
routerName
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
resolveDir: args.resolveDir
|
|
181
|
+
console.log("[Optimized]");
|
|
182
|
+
console.log(` Router: ${routerName}`);
|
|
183
|
+
const outfile = resolve(process.cwd(), options.outfile);
|
|
184
|
+
await esbuild2.build({
|
|
185
|
+
entryPoints: [appFilePath],
|
|
186
|
+
outfile,
|
|
187
|
+
bundle: true,
|
|
188
|
+
minify: options.minify,
|
|
189
|
+
format: "esm",
|
|
190
|
+
target: options.target,
|
|
191
|
+
platform: "node",
|
|
192
|
+
jsx: "automatic",
|
|
193
|
+
jsxImportSource: "hono/jsx",
|
|
194
|
+
plugins: [
|
|
195
|
+
{
|
|
196
|
+
name: "hono-optimize",
|
|
197
|
+
setup(build2) {
|
|
198
|
+
const honoPseudoImportPath = "hono-optimized-pseudo-import-path";
|
|
199
|
+
build2.onResolve({ filter: /^hono$/ }, async (args) => {
|
|
200
|
+
if (!args.importer) {
|
|
201
|
+
return void 0;
|
|
202
|
+
}
|
|
203
|
+
const resolved = await build2.resolve(args.path, {
|
|
204
|
+
kind: "import-statement",
|
|
205
|
+
resolveDir: args.resolveDir
|
|
206
|
+
});
|
|
207
|
+
return {
|
|
208
|
+
path: join(dirname(resolved.path), honoPseudoImportPath)
|
|
209
|
+
};
|
|
173
210
|
});
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
});
|
|
178
|
-
build3.onLoad({ filter: new RegExp(`/${honoPseudoImportPath}$`) }, async () => {
|
|
179
|
-
return {
|
|
180
|
-
contents: `
|
|
211
|
+
build2.onLoad({ filter: new RegExp(`/${honoPseudoImportPath}$`) }, async () => {
|
|
212
|
+
return {
|
|
213
|
+
contents: `
|
|
181
214
|
import { HonoBase } from 'hono/hono-base'
|
|
182
215
|
${importStatement}
|
|
183
216
|
export class Hono extends HonoBase {
|
|
@@ -187,15 +220,16 @@ export class Hono extends HonoBase {
|
|
|
187
220
|
}
|
|
188
221
|
}
|
|
189
222
|
`
|
|
190
|
-
|
|
191
|
-
|
|
223
|
+
};
|
|
224
|
+
});
|
|
225
|
+
}
|
|
192
226
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
227
|
+
]
|
|
228
|
+
});
|
|
229
|
+
const outfileStat = statSync(outfile);
|
|
230
|
+
console.log(` Output: ${options.outfile} (${(outfileStat.size / 1024).toFixed(2)} KB)`);
|
|
231
|
+
}
|
|
232
|
+
);
|
|
199
233
|
}
|
|
200
234
|
|
|
201
235
|
// src/commands/request/index.ts
|
|
@@ -203,7 +237,7 @@ import { existsSync as existsSync2, realpathSync as realpathSync2 } from "fs";
|
|
|
203
237
|
import { resolve as resolve2 } from "path";
|
|
204
238
|
var DEFAULT_ENTRY_CANDIDATES2 = ["src/index.ts", "src/index.tsx", "src/index.js", "src/index.jsx"];
|
|
205
239
|
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(
|
|
240
|
+
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
241
|
"-H, --header <header>",
|
|
208
242
|
"Custom headers",
|
|
209
243
|
(value, previous) => {
|
|
@@ -212,11 +246,15 @@ function requestCommand(program2) {
|
|
|
212
246
|
[]
|
|
213
247
|
).action(async (file, options) => {
|
|
214
248
|
const path = options.path || "/";
|
|
215
|
-
const
|
|
216
|
-
|
|
249
|
+
const watch = options.watch;
|
|
250
|
+
const buildIterator = getBuildIterator(file, watch);
|
|
251
|
+
for await (const app of buildIterator) {
|
|
252
|
+
const result = await executeRequest(app, path, options);
|
|
253
|
+
console.log(JSON.stringify(result, null, 2));
|
|
254
|
+
}
|
|
217
255
|
});
|
|
218
256
|
}
|
|
219
|
-
|
|
257
|
+
function getBuildIterator(appPath, watch) {
|
|
220
258
|
let entry;
|
|
221
259
|
let resolvedAppPath;
|
|
222
260
|
if (appPath) {
|
|
@@ -230,12 +268,12 @@ async function executeRequest(appPath, requestPath, options) {
|
|
|
230
268
|
throw new Error(`Entry file ${entry} does not exist`);
|
|
231
269
|
}
|
|
232
270
|
const appFilePath = realpathSync2(resolvedAppPath);
|
|
233
|
-
|
|
234
|
-
external: ["@hono/node-server"]
|
|
271
|
+
return buildAndImportApp(appFilePath, {
|
|
272
|
+
external: ["@hono/node-server"],
|
|
273
|
+
watch
|
|
235
274
|
});
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
275
|
+
}
|
|
276
|
+
async function executeRequest(app, requestPath, options) {
|
|
239
277
|
const url = new URL(requestPath, "http://localhost");
|
|
240
278
|
const requestInit = {
|
|
241
279
|
method: options.method || "GET"
|
|
@@ -495,9 +533,10 @@ function serveCommand(program2) {
|
|
|
495
533
|
app = new Hono();
|
|
496
534
|
} else {
|
|
497
535
|
const appFilePath = realpathSync3(appPath);
|
|
498
|
-
|
|
536
|
+
const buildIterator = buildAndImportApp(appFilePath, {
|
|
499
537
|
external: ["@hono/node-server"]
|
|
500
538
|
});
|
|
539
|
+
app = (await buildIterator.next()).value;
|
|
501
540
|
}
|
|
502
541
|
}
|
|
503
542
|
const allFunctions = {};
|