@web-ts-toolkit/express-runtime 0.24.0 → 0.26.0
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 +96 -41
- package/cli.js +121 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,10 +23,12 @@ pnpm add @web-ts-toolkit/express-runtime express
|
|
|
23
23
|
- `startLocalServer()` — `http.createServer` + `listen` with friendly
|
|
24
24
|
`EADDRINUSE` / `EACCES` errors, optional graceful `SIGINT` / `SIGTERM`
|
|
25
25
|
shutdown that drains in-flight requests, and a configurable timeout.
|
|
26
|
-
- CLI binary with
|
|
26
|
+
- CLI binary with five subcommands:
|
|
27
27
|
- `dev` — run an Express app as a local dev server
|
|
28
|
-
- `build` — bundle the app as a
|
|
29
|
-
- `start` —
|
|
28
|
+
- `build` — bundle the app as a local runtime module
|
|
29
|
+
- `start` — start the bundled local app module
|
|
30
|
+
- `build-serverless` — bundle the app as a serverless handler
|
|
31
|
+
- `start-serverless` — smoke-test the bundled handler locally by translating HTTP ↔ serverless events
|
|
30
32
|
|
|
31
33
|
## Quick Start
|
|
32
34
|
|
|
@@ -131,19 +133,19 @@ npx tsx ./node_modules/@web-ts-toolkit/express-runtime/dist/cli.js dev ./src/app
|
|
|
131
133
|
> be placed at the top level of your app module since `dev` does not expose an
|
|
132
134
|
> `init` hook.
|
|
133
135
|
|
|
134
|
-
### CLI — build (
|
|
136
|
+
### CLI — build (local runtime bundle)
|
|
135
137
|
|
|
136
|
-
The `build` command generates a temporary
|
|
137
|
-
|
|
138
|
+
The `build` command generates a temporary runtime entry that re-exports the app
|
|
139
|
+
and optional `init` hook, then bundles it into a local runtime file:
|
|
138
140
|
|
|
139
141
|
```sh
|
|
140
|
-
npx wtt-express-runtime build ./src/app.ts --out-dir
|
|
142
|
+
npx wtt-express-runtime build ./src/app.ts --out-dir dist
|
|
141
143
|
```
|
|
142
144
|
|
|
143
145
|
With an optional init hook (DB connections, cache warmup, etc.):
|
|
144
146
|
|
|
145
147
|
```sh
|
|
146
|
-
npx wtt-express-runtime build ./src/app.ts --init ./src/init.ts --out-dir
|
|
148
|
+
npx wtt-express-runtime build ./src/app.ts --init ./src/init.ts --out-dir dist
|
|
147
149
|
```
|
|
148
150
|
|
|
149
151
|
```ts
|
|
@@ -153,29 +155,54 @@ export default async () => {
|
|
|
153
155
|
};
|
|
154
156
|
```
|
|
155
157
|
|
|
156
|
-
This produces `
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
This produces `dist/app.js` (configurable via `--out-name`) that default-exports
|
|
159
|
+
the Express app and, when `--init` is used, also exports `init` for the `start`
|
|
160
|
+
command to run before listening.
|
|
159
161
|
|
|
160
162
|
> `express` is always external; additional externals can be added via
|
|
161
163
|
> `--external`.
|
|
162
164
|
|
|
163
|
-
### CLI — start (run a bundled
|
|
165
|
+
### CLI — start (run a bundled app locally)
|
|
164
166
|
|
|
165
|
-
The `start` command runs
|
|
166
|
-
HTTP requests into serverless events and the handler's results back into HTTP
|
|
167
|
-
responses — letting you smoke-test the exact `build` output without a
|
|
168
|
-
serverless platform:
|
|
167
|
+
The `start` command runs the `build` output locally with `startLocalServer()`:
|
|
169
168
|
|
|
170
169
|
```sh
|
|
171
170
|
npx wtt-express-runtime build ./src/app.ts --out-dir dist
|
|
172
|
-
npx wtt-express-runtime start ./dist/
|
|
171
|
+
npx wtt-express-runtime start ./dist/app.js --port 9000 --env .env
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The bundled app module must default-export an Express app (or export it as
|
|
175
|
+
`app`). If it exports `init`, that hook runs once before the server starts
|
|
176
|
+
listening.
|
|
177
|
+
|
|
178
|
+
### CLI — build-serverless (serverless bundle)
|
|
179
|
+
|
|
180
|
+
The `build-serverless` command preserves the previous serverless bundling flow:
|
|
181
|
+
|
|
182
|
+
```sh
|
|
183
|
+
npx wtt-express-runtime build-serverless ./src/app.ts --out-dir netlify/functions
|
|
173
184
|
```
|
|
174
185
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
186
|
+
With an optional init hook:
|
|
187
|
+
|
|
188
|
+
```sh
|
|
189
|
+
npx wtt-express-runtime build-serverless ./src/app.ts --init ./src/init.ts --out-dir netlify/functions
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
This produces `netlify/functions/handler.js` (configurable via `--out-name`)
|
|
193
|
+
that exports a `handler` function compatible with Netlify, Vercel, AWS Lambda,
|
|
194
|
+
and any platform that calls `(event, context)`.
|
|
195
|
+
|
|
196
|
+
### CLI — start-serverless (run a bundled handler locally)
|
|
197
|
+
|
|
198
|
+
The `start-serverless` command runs a bundled serverless handler locally by
|
|
199
|
+
translating HTTP requests into serverless events and the handler's results back
|
|
200
|
+
into HTTP responses:
|
|
201
|
+
|
|
202
|
+
```sh
|
|
203
|
+
npx wtt-express-runtime build-serverless ./src/app.ts --out-dir dist
|
|
204
|
+
npx wtt-express-runtime start-serverless ./dist/handler.js --port 9000 --env .env
|
|
205
|
+
```
|
|
179
206
|
|
|
180
207
|
> The adapter passes the raw request body as a Buffer (no body parsing) so the
|
|
181
208
|
> handler's request hook, including the serverless-http #305 workaround, runs
|
|
@@ -298,11 +325,13 @@ interface Logger {
|
|
|
298
325
|
|
|
299
326
|
Omitting `<command>` defaults to `dev` for backward compatibility.
|
|
300
327
|
|
|
301
|
-
| Command
|
|
302
|
-
|
|
|
303
|
-
| `dev`
|
|
304
|
-
| `build`
|
|
305
|
-
| `start`
|
|
328
|
+
| Command | Description |
|
|
329
|
+
| ------------------ | ----------------------------------------------------------------------------------- |
|
|
330
|
+
| `dev` | Run the Express app as a local dev server (`http.createServer` + graceful shutdown) |
|
|
331
|
+
| `build` | Bundle the Express app as a local runtime module |
|
|
332
|
+
| `start` | Run a bundled local app module with `startLocalServer()` |
|
|
333
|
+
| `build-serverless` | Bundle the Express app as a serverless handler |
|
|
334
|
+
| `start-serverless` | Run a bundled serverless handler locally (HTTP ↔ serverless event adapter) |
|
|
306
335
|
|
|
307
336
|
#### dev options
|
|
308
337
|
|
|
@@ -326,7 +355,7 @@ Omitting `<command>` defaults to `dev` for backward compatibility.
|
|
|
326
355
|
| `<app-module>` | Module path whose **default export** is an Express app (sync, not async factory) |
|
|
327
356
|
| `--init <path>` | Init hook module (default export, async function) called once per cold start |
|
|
328
357
|
| `--out-dir <path>` | Output directory (default: `dist`) |
|
|
329
|
-
| `--out-name <name>` | Output filename without extension (default: `
|
|
358
|
+
| `--out-name <name>` | Output filename without extension (default: `app`) |
|
|
330
359
|
| `--format <cjs\|esm>` | Output format (default: `cjs`) |
|
|
331
360
|
| `--target <target>` | Compilation target (default: `node22`) |
|
|
332
361
|
| `--external <pkg>` | Mark package as external (repeatable; `express` is always external) |
|
|
@@ -334,15 +363,40 @@ Omitting `<command>` defaults to `dev` for backward compatibility.
|
|
|
334
363
|
|
|
335
364
|
#### start options
|
|
336
365
|
|
|
337
|
-
| Option | Description
|
|
338
|
-
| ------------------------- |
|
|
339
|
-
| `<
|
|
340
|
-
| `--port <number>` | Port or named pipe (default: `process.env.PORT` or `8080`)
|
|
341
|
-
| `--host <hostname>` | Hostname to bind (default: `process.env.HOST` or `0.0.0.0`)
|
|
342
|
-
| `--no-signals` | Disable `SIGINT` / `SIGTERM` handler registration
|
|
343
|
-
| `--shutdown-timeout <ms>` | Max ms to wait for in-flight requests (default: `5000`)
|
|
344
|
-
| `--require <module>` | Module(s) to preload before
|
|
345
|
-
| `--env <path>` | Env file(s) to load before
|
|
366
|
+
| Option | Description |
|
|
367
|
+
| ------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
368
|
+
| `<app-module>` | JS/CJS module path default-exporting an Express app (or exporting `app`) — the output of `build` |
|
|
369
|
+
| `--port <number>` | Port or named pipe (default: `process.env.PORT` or `8080`) |
|
|
370
|
+
| `--host <hostname>` | Hostname to bind (default: `process.env.HOST` or `0.0.0.0`) |
|
|
371
|
+
| `--no-signals` | Disable `SIGINT` / `SIGTERM` handler registration |
|
|
372
|
+
| `--shutdown-timeout <ms>` | Max ms to wait for in-flight requests (default: `5000`) |
|
|
373
|
+
| `--require <module>` | Module(s) to preload before app load (repeatable; comma-separated values supported) |
|
|
374
|
+
| `--env <path>` | Env file(s) to load before app load (repeatable; existing env vars are not overridden) |
|
|
375
|
+
|
|
376
|
+
#### build-serverless options
|
|
377
|
+
|
|
378
|
+
| Option | Description |
|
|
379
|
+
| --------------------- | -------------------------------------------------------------------------------- |
|
|
380
|
+
| `<app-module>` | Module path whose **default export** is an Express app (sync, not async factory) |
|
|
381
|
+
| `--init <path>` | Init hook module (default export, async function) called once per cold start |
|
|
382
|
+
| `--out-dir <path>` | Output directory (default: `dist`) |
|
|
383
|
+
| `--out-name <name>` | Output filename without extension (default: `handler`) |
|
|
384
|
+
| `--format <cjs\|esm>` | Output format (default: `cjs`) |
|
|
385
|
+
| `--target <target>` | Compilation target (default: `node22`) |
|
|
386
|
+
| `--external <pkg>` | Mark package as external (repeatable; `express` is always external) |
|
|
387
|
+
| `--no-clean` | Don't clean the output directory before building |
|
|
388
|
+
|
|
389
|
+
#### start-serverless options
|
|
390
|
+
|
|
391
|
+
| Option | Description |
|
|
392
|
+
| ------------------------- | -------------------------------------------------------------------------------------------- |
|
|
393
|
+
| `<handler-module>` | JS/CJS module path exporting `handler` (named or default) — the output of `build-serverless` |
|
|
394
|
+
| `--port <number>` | Port or named pipe (default: `process.env.PORT` or `8080`) |
|
|
395
|
+
| `--host <hostname>` | Hostname to bind (default: `process.env.HOST` or `0.0.0.0`) |
|
|
396
|
+
| `--no-signals` | Disable `SIGINT` / `SIGTERM` handler registration |
|
|
397
|
+
| `--shutdown-timeout <ms>` | Max ms to wait for in-flight requests (default: `5000`) |
|
|
398
|
+
| `--require <module>` | Module(s) to preload before handler load (repeatable; comma-separated values supported) |
|
|
399
|
+
| `--env <path>` | Env file(s) to load before handler load (repeatable; existing env vars are not overridden) |
|
|
346
400
|
|
|
347
401
|
#### global options
|
|
348
402
|
|
|
@@ -355,11 +409,12 @@ The `dev` command sets `exitAfterShutdown: true` so `SIGINT` / `SIGTERM` cleanly
|
|
|
355
409
|
exit the process after the server drains. TypeScript app modules require a TS
|
|
356
410
|
loader (see the Quick Start CLI section for a `tsx` invocation).
|
|
357
411
|
|
|
358
|
-
The `build` command generates a temporary entry file that
|
|
359
|
-
module and
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
bundled into the output unless marked external via
|
|
412
|
+
The `build` command generates a temporary entry file that re-exports the app
|
|
413
|
+
module and optional `init` hook, then produces a local runtime bundle. The
|
|
414
|
+
`build-serverless` command instead wraps the app with `createServerlessHandler`
|
|
415
|
+
and bundles the serverless runtime. `express` is always external; all other
|
|
416
|
+
dependencies are bundled into the output unless marked external via
|
|
417
|
+
`--external`.
|
|
363
418
|
|
|
364
419
|
## License
|
|
365
420
|
|
package/cli.js
CHANGED
|
@@ -208,7 +208,7 @@ function readValue(argv, index, name) {
|
|
|
208
208
|
function printHelp() {
|
|
209
209
|
console.log(`wtt-express-runtime
|
|
210
210
|
|
|
211
|
-
Run an Express app locally, bundle it
|
|
211
|
+
Run an Express app locally, bundle it for local or serverless runtimes, or run the bundle.
|
|
212
212
|
|
|
213
213
|
Usage:
|
|
214
214
|
wtt-express-runtime <command> <app-module> [options]
|
|
@@ -216,8 +216,10 @@ Usage:
|
|
|
216
216
|
|
|
217
217
|
Commands:
|
|
218
218
|
dev Run the Express app as a local dev server
|
|
219
|
-
build Bundle the Express app as a
|
|
220
|
-
start Run a bundled
|
|
219
|
+
build Bundle the Express app as a local app module
|
|
220
|
+
start Run a bundled local app module
|
|
221
|
+
build-serverless Bundle the Express app as a serverless handler
|
|
222
|
+
start-serverless Run a bundled serverless handler locally
|
|
221
223
|
|
|
222
224
|
Dev options:
|
|
223
225
|
--port <number> Port or named pipe (default: process.env.PORT or 8080)
|
|
@@ -233,13 +235,30 @@ Dev options:
|
|
|
233
235
|
Build options:
|
|
234
236
|
--init <path> Init hook module (default export, async function)
|
|
235
237
|
--out-dir <path> Output directory (default: dist)
|
|
236
|
-
--out-name <name> Output filename without extension (default:
|
|
238
|
+
--out-name <name> Output filename without extension (default: app)
|
|
237
239
|
--format <cjs|esm> Output format (default: cjs)
|
|
238
240
|
--target <target> Compilation target (default: node22)
|
|
239
241
|
--external <pkg> Mark package as external (repeatable; express always external)
|
|
240
242
|
--no-clean Don't clean the output directory before building
|
|
241
243
|
|
|
242
244
|
Start options:
|
|
245
|
+
--port <number> Port or named pipe (default: process.env.PORT or 8080)
|
|
246
|
+
--host <hostname> Hostname to bind (default: process.env.HOST or 0.0.0.0)
|
|
247
|
+
--no-signals Disable SIGINT/SIGTERM handler registration
|
|
248
|
+
--shutdown-timeout <ms> Max ms to wait for in-flight requests (default: 5000)
|
|
249
|
+
--require <module> Module(s) to preload before app load (repeatable)
|
|
250
|
+
--env <path> Env file(s) to load (repeatable; existing env vars are not overridden)
|
|
251
|
+
|
|
252
|
+
Build-serverless options:
|
|
253
|
+
--init <path> Init hook module (default export, async function)
|
|
254
|
+
--out-dir <path> Output directory (default: dist)
|
|
255
|
+
--out-name <name> Output filename without extension (default: handler)
|
|
256
|
+
--format <cjs|esm> Output format (default: cjs)
|
|
257
|
+
--target <target> Compilation target (default: node22)
|
|
258
|
+
--external <pkg> Mark package as external (repeatable; express always external)
|
|
259
|
+
--no-clean Don't clean the output directory before building
|
|
260
|
+
|
|
261
|
+
Start-serverless options:
|
|
243
262
|
--port <number> Port or named pipe (default: process.env.PORT or 8080)
|
|
244
263
|
--host <hostname> Hostname to bind (default: process.env.HOST or 0.0.0.0)
|
|
245
264
|
--no-signals Disable SIGINT/SIGTERM handler registration
|
|
@@ -255,10 +274,12 @@ Examples:
|
|
|
255
274
|
wtt-express-runtime dev ./dist/app.js
|
|
256
275
|
wtt-express-runtime dev ./dist/app.js --port 3000 --host localhost
|
|
257
276
|
wtt-express-runtime dev ./src/app.ts --env .env --require tsconfig-paths/register --watch ./src,./shared
|
|
258
|
-
wtt-express-runtime build ./src/app.ts --out-dir
|
|
259
|
-
wtt-express-runtime
|
|
260
|
-
wtt-express-runtime
|
|
261
|
-
wtt-express-runtime build ./src/app.ts
|
|
277
|
+
wtt-express-runtime build ./src/app.ts --out-dir dist
|
|
278
|
+
wtt-express-runtime start ./dist/app.js --port 3000 --env .env
|
|
279
|
+
wtt-express-runtime build-serverless ./src/app.ts --out-dir netlify/functions
|
|
280
|
+
wtt-express-runtime build-serverless ./src/app.ts --init ./src/init.ts --format esm
|
|
281
|
+
wtt-express-runtime start-serverless ./netlify/functions/handler.js --port 9000 --env .env
|
|
282
|
+
wtt-express-runtime build-serverless ./src/app.ts && wtt-express-runtime start-serverless ./dist/handler.js
|
|
262
283
|
|
|
263
284
|
Notes:
|
|
264
285
|
- In dev mode, the CLI evaluates arbitrary code from <app-module> in the current process.
|
|
@@ -269,9 +290,11 @@ Notes:
|
|
|
269
290
|
For advanced dotenv features (multiline, expansion), --require dotenv/config instead.
|
|
270
291
|
- --watch forks a child process running the same CLI without --watch. On file change,
|
|
271
292
|
the child is killed (SIGTERM) and respawned after the debounce delay.
|
|
272
|
-
- In build mode, express is always external. Add more externals with --external.
|
|
273
|
-
- In start mode, the bundled
|
|
274
|
-
|
|
293
|
+
- In build/build-serverless mode, express is always external. Add more externals with --external.
|
|
294
|
+
- In start mode, the bundled app file must default-export an Express app (or export it as "app").
|
|
295
|
+
If the bundle exports "init", it runs before the server starts listening.
|
|
296
|
+
- In start-serverless mode, the bundled handler file must be a JS/CJS module whose
|
|
297
|
+
"handler" export (or default export) is a function: (event, context) => Promise<result>.
|
|
275
298
|
- Init logic for dev mode (DB connections, etc.): add at the top level of your app module.
|
|
276
299
|
`);
|
|
277
300
|
}
|
|
@@ -282,7 +305,7 @@ function isHelp(arg) {
|
|
|
282
305
|
return arg === "-h" || arg === "--help";
|
|
283
306
|
}
|
|
284
307
|
function isSubcommand(arg) {
|
|
285
|
-
return arg === "dev" || arg === "build" || arg === "start";
|
|
308
|
+
return arg === "dev" || arg === "build" || arg === "start" || arg === "build-serverless" || arg === "start-serverless";
|
|
286
309
|
}
|
|
287
310
|
var DEFAULT_WATCH_EXTENSIONS = ["ts", "js", "mjs", "cjs", "json"];
|
|
288
311
|
var DEFAULT_WATCH_DELAY = 500;
|
|
@@ -423,13 +446,25 @@ function parseDevArgs(argv) {
|
|
|
423
446
|
watchDelay: watchDelay ?? DEFAULT_WATCH_DELAY
|
|
424
447
|
};
|
|
425
448
|
}
|
|
426
|
-
function
|
|
449
|
+
function parseStartLikeArgs(argv, subcommandName) {
|
|
427
450
|
for (const arg of argv) {
|
|
428
451
|
if (arg === "--watch" || arg.startsWith("--watch=") || arg === "--ext" || arg.startsWith("--ext=") || arg === "--delay" || arg.startsWith("--delay=")) {
|
|
429
|
-
throw new Error(`--watch/--ext/--delay are not supported with the
|
|
452
|
+
throw new Error(`--watch/--ext/--delay are not supported with the ${subcommandName} subcommand`);
|
|
430
453
|
}
|
|
431
454
|
}
|
|
432
|
-
|
|
455
|
+
return parseDevArgs(argv);
|
|
456
|
+
}
|
|
457
|
+
function parseStartArgs(argv) {
|
|
458
|
+
const result = parseStartLikeArgs(argv, "start");
|
|
459
|
+
return {
|
|
460
|
+
appPath: result.appPath,
|
|
461
|
+
options: result.options,
|
|
462
|
+
require: result.require,
|
|
463
|
+
env: result.env
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
function parseStartServerlessArgs(argv) {
|
|
467
|
+
const result = parseStartLikeArgs(argv, "start-serverless");
|
|
433
468
|
return {
|
|
434
469
|
handlerPath: result.appPath,
|
|
435
470
|
options: result.options,
|
|
@@ -437,13 +472,13 @@ function parseStartArgs(argv) {
|
|
|
437
472
|
env: result.env
|
|
438
473
|
};
|
|
439
474
|
}
|
|
440
|
-
function parseBuildArgs(argv) {
|
|
475
|
+
function parseBuildArgs(argv, outNameDefault) {
|
|
441
476
|
let appPath;
|
|
442
477
|
const external = [];
|
|
443
478
|
const result = {
|
|
444
479
|
initPath: void 0,
|
|
445
480
|
outDir: "dist",
|
|
446
|
-
outName:
|
|
481
|
+
outName: outNameDefault,
|
|
447
482
|
format: "cjs",
|
|
448
483
|
target: "node22",
|
|
449
484
|
external,
|
|
@@ -538,6 +573,12 @@ function parseBuildArgs(argv) {
|
|
|
538
573
|
}
|
|
539
574
|
return { appPath, ...result };
|
|
540
575
|
}
|
|
576
|
+
function parseLocalBuildArgs(argv) {
|
|
577
|
+
return parseBuildArgs(argv, "app");
|
|
578
|
+
}
|
|
579
|
+
function parseBuildServerlessArgs(argv) {
|
|
580
|
+
return parseBuildArgs(argv, "handler");
|
|
581
|
+
}
|
|
541
582
|
function parseArgs(argv) {
|
|
542
583
|
if (argv.length === 0) {
|
|
543
584
|
printHelp();
|
|
@@ -557,10 +598,16 @@ function parseArgs(argv) {
|
|
|
557
598
|
if (first === "dev") {
|
|
558
599
|
return { subcommand: "dev", dev: parseDevArgs(rest) };
|
|
559
600
|
}
|
|
601
|
+
if (first === "build") {
|
|
602
|
+
return { subcommand: "build", build: parseLocalBuildArgs(rest) };
|
|
603
|
+
}
|
|
560
604
|
if (first === "start") {
|
|
561
605
|
return { subcommand: "start", start: parseStartArgs(rest) };
|
|
562
606
|
}
|
|
563
|
-
|
|
607
|
+
if (first === "build-serverless") {
|
|
608
|
+
return { subcommand: "build-serverless", buildServerless: parseBuildServerlessArgs(rest) };
|
|
609
|
+
}
|
|
610
|
+
return { subcommand: "start-serverless", startServerless: parseStartServerlessArgs(rest) };
|
|
564
611
|
}
|
|
565
612
|
return { subcommand: "dev", dev: parseDevArgs(argv) };
|
|
566
613
|
}
|
|
@@ -716,7 +763,8 @@ function runWithWatch(args) {
|
|
|
716
763
|
process.on("SIGTERM", shutdown);
|
|
717
764
|
spawnChild();
|
|
718
765
|
}
|
|
719
|
-
var
|
|
766
|
+
var TEMP_BUILD_ENTRY_FILENAME = ".express-runtime-build-entry.ts";
|
|
767
|
+
var TEMP_SERVERLESS_ENTRY_FILENAME = ".express-runtime-build-serverless-entry.ts";
|
|
720
768
|
function generateServerlessEntry(appPath, initPath) {
|
|
721
769
|
const absAppPath = (0, import_node_path.resolve)(process.cwd(), appPath);
|
|
722
770
|
const absInitPath = initPath ? (0, import_node_path.resolve)(process.cwd(), initPath) : void 0;
|
|
@@ -734,16 +782,28 @@ function generateServerlessEntry(appPath, initPath) {
|
|
|
734
782
|
lines.push(`export { handler };`);
|
|
735
783
|
return lines.join("\n") + "\n";
|
|
736
784
|
}
|
|
737
|
-
|
|
785
|
+
function generateRuntimeEntry(appPath, initPath) {
|
|
786
|
+
const absAppPath = (0, import_node_path.resolve)(process.cwd(), appPath);
|
|
787
|
+
const absInitPath = initPath ? (0, import_node_path.resolve)(process.cwd(), initPath) : void 0;
|
|
788
|
+
const lines = [
|
|
789
|
+
"// Auto-generated by @web-ts-toolkit/express-runtime CLI \u2014 do not edit.",
|
|
790
|
+
`import app from ${JSON.stringify(absAppPath)};`,
|
|
791
|
+
"export default app;",
|
|
792
|
+
"export { app };"
|
|
793
|
+
];
|
|
794
|
+
if (absInitPath) {
|
|
795
|
+
lines.push(`export { default as init } from ${JSON.stringify(absInitPath)};`);
|
|
796
|
+
}
|
|
797
|
+
return lines.join("\n") + "\n";
|
|
798
|
+
}
|
|
799
|
+
async function buildBundle(args, tempEntryFilename, entryContent) {
|
|
738
800
|
const tsupModule = await import("tsup");
|
|
739
801
|
const { build } = tsupModule;
|
|
740
|
-
const
|
|
741
|
-
const tempEntryPath = (0, import_node_path.resolve)(process.cwd(), TEMP_ENTRY_FILENAME);
|
|
802
|
+
const tempEntryPath = (0, import_node_path.resolve)(process.cwd(), tempEntryFilename);
|
|
742
803
|
(0, import_node_fs.writeFileSync)(tempEntryPath, entryContent, "utf8");
|
|
743
804
|
try {
|
|
744
805
|
await build({
|
|
745
806
|
config: false,
|
|
746
|
-
// Don't auto-load tsup.config.ts from cwd
|
|
747
807
|
entry: { [args.outName]: tempEntryPath },
|
|
748
808
|
format: [args.format],
|
|
749
809
|
target: args.target,
|
|
@@ -758,6 +818,12 @@ async function buildServerless(args) {
|
|
|
758
818
|
(0, import_node_fs.rmSync)(tempEntryPath, { force: true });
|
|
759
819
|
}
|
|
760
820
|
}
|
|
821
|
+
async function buildRuntime(args) {
|
|
822
|
+
await buildBundle(args, TEMP_BUILD_ENTRY_FILENAME, generateRuntimeEntry(args.appPath, args.initPath));
|
|
823
|
+
}
|
|
824
|
+
async function buildServerless(args) {
|
|
825
|
+
await buildBundle(args, TEMP_SERVERLESS_ENTRY_FILENAME, generateServerlessEntry(args.appPath, args.initPath));
|
|
826
|
+
}
|
|
761
827
|
function collectBody(req) {
|
|
762
828
|
return new Promise((resolve, reject) => {
|
|
763
829
|
const chunks = [];
|
|
@@ -816,6 +882,25 @@ function createServerlessAdapterApp(handler) {
|
|
|
816
882
|
}
|
|
817
883
|
});
|
|
818
884
|
}
|
|
885
|
+
async function loadBuiltApp(appPath) {
|
|
886
|
+
const fullPath = (0, import_node_path.resolve)(process.cwd(), appPath);
|
|
887
|
+
const moduleUrl = (0, import_node_url.pathToFileURL)(fullPath).href;
|
|
888
|
+
const mod = await import(moduleUrl);
|
|
889
|
+
const exported = extractExport(mod);
|
|
890
|
+
if (!exported) {
|
|
891
|
+
throw new Error(
|
|
892
|
+
`Module "${appPath}" must default-export an Express app or export it as "app". Exports: ${Object.keys(mod).join(", ")}`
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
const init = mod.init;
|
|
896
|
+
if (init !== void 0 && typeof init !== "function") {
|
|
897
|
+
throw new Error(`Module "${appPath}" must export "init" as a function when present.`);
|
|
898
|
+
}
|
|
899
|
+
return {
|
|
900
|
+
app: await resolveExport(exported, appPath),
|
|
901
|
+
init
|
|
902
|
+
};
|
|
903
|
+
}
|
|
819
904
|
async function loadHandler(handlerPath) {
|
|
820
905
|
const fullPath = (0, import_node_path.resolve)(process.cwd(), handlerPath);
|
|
821
906
|
const moduleUrl = (0, import_node_url.pathToFileURL)(fullPath).href;
|
|
@@ -853,11 +938,21 @@ async function main() {
|
|
|
853
938
|
loadEnvFiles(start.env);
|
|
854
939
|
}
|
|
855
940
|
await preloadModules(start.require);
|
|
856
|
-
const
|
|
941
|
+
const { app, init } = await loadBuiltApp(start.appPath);
|
|
942
|
+
startLocalServer(app, { ...start.options, init, exitAfterShutdown: true });
|
|
943
|
+
} else if (parsedArgs.subcommand === "build") {
|
|
944
|
+
await buildRuntime(parsedArgs.build);
|
|
945
|
+
} else if (parsedArgs.subcommand === "start-serverless") {
|
|
946
|
+
const { startServerless } = parsedArgs;
|
|
947
|
+
if (startServerless.env.length > 0) {
|
|
948
|
+
loadEnvFiles(startServerless.env);
|
|
949
|
+
}
|
|
950
|
+
await preloadModules(startServerless.require);
|
|
951
|
+
const handler = await loadHandler(startServerless.handlerPath);
|
|
857
952
|
const app = createServerlessAdapterApp(handler);
|
|
858
|
-
startLocalServer(app, { ...
|
|
953
|
+
startLocalServer(app, { ...startServerless.options, exitAfterShutdown: true });
|
|
859
954
|
} else {
|
|
860
|
-
await buildServerless(parsedArgs.
|
|
955
|
+
await buildServerless(parsedArgs.buildServerless);
|
|
861
956
|
}
|
|
862
957
|
}
|
|
863
958
|
main().catch((error) => {
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@web-ts-toolkit/express-runtime",
|
|
3
3
|
"description": "Express app factory plus serverless handler and local dev server helpers",
|
|
4
4
|
"homepage": "https://web-ts-toolkit.pages.dev/docs/packages/express-runtime",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.26.0",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"keywords": [
|
|
8
8
|
"express",
|