@vercel/cervel 0.0.11 → 0.0.13

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/dist/cli.mjs CHANGED
@@ -1,587 +1,6 @@
1
- import { builtinModules, createRequire } from "node:module";
2
1
  import { parseArgs } from "node:util";
3
- import { existsSync } from "node:fs";
4
- import { lstat, readFile, rm } from "node:fs/promises";
5
- import { dirname, extname, join, relative } from "node:path";
6
- import { build } from "rolldown";
7
- import { exports } from "resolve.exports";
8
- import { isNativeError } from "node:util/types";
9
- import { FileFsRef, debug, glob } from "@vercel/build-utils";
10
- import { nodeFileTrace, resolve } from "@vercel/nft";
11
- import { transform } from "oxc-transform";
12
- import { createRequire as createRequire$1 } from "module";
13
- import { spawn } from "child_process";
14
- import { extname as extname$1, join as join$1 } from "path";
15
- import { existsSync as existsSync$1 } from "fs";
16
- import execa from "execa";
17
- import { writeFile } from "fs/promises";
2
+ import { cervelBuild as build, cervelServe as serve, srvxOptions } from "@vercel/backends";
18
3
 
19
- //#region src/node-file-trace.ts
20
- const nodeFileTrace$1 = async (args) => {
21
- const files = {};
22
- const { tracedPaths } = args;
23
- const compiledSourceFiles = await glob("**/*", {
24
- cwd: args.outDir,
25
- follow: true,
26
- includeDirectories: true
27
- });
28
- for (const file of Object.keys(compiledSourceFiles)) files[file] = compiledSourceFiles[file];
29
- /**
30
- * While we're not using NFT to process source code, we are using it
31
- * to tree shake node deps, and include any fs reads for files that are
32
- * not part of the traced paths or compiled source files.
33
- * Most of this is identical to the `@vercel/node` implementation
34
- */
35
- const result = await nodeFileTrace(Array.from(tracedPaths), {
36
- base: args.repoRootPath,
37
- processCwd: args.workPath,
38
- ts: true,
39
- mixedModules: true,
40
- async resolve(id, parent, job, cjsResolve) {
41
- return resolve(id, parent, job, cjsResolve);
42
- },
43
- async readFile(fsPath) {
44
- try {
45
- let source = await readFile(fsPath);
46
- if (fsPath.endsWith(".ts") && !fsPath.endsWith(".d.ts") || fsPath.endsWith(".tsx") || fsPath.endsWith(".mts") || fsPath.endsWith(".cts")) source = (await transform(fsPath, source.toString())).code;
47
- return source;
48
- } catch (error) {
49
- if (isNativeError(error) && "code" in error && (error.code === "ENOENT" || error.code === "EISDIR")) return null;
50
- throw error;
51
- }
52
- }
53
- });
54
- if (!args.keepTracedPaths) for (const file of tracedPaths) {
55
- const relativeFile = relative(args.repoRootPath, file);
56
- result.fileList.delete(relativeFile);
57
- }
58
- debug("NFT traced files count:", result.fileList.size);
59
- for (const file of result.fileList) {
60
- const absolutePath = join(args.repoRootPath, file);
61
- const stats = await lstat(absolutePath);
62
- const outputPath = file;
63
- if (stats.isSymbolicLink() || stats.isFile()) files[outputPath] = new FileFsRef({
64
- fsPath: absolutePath,
65
- mode: stats.mode
66
- });
67
- }
68
- debug("Total files in context:", Object.keys(files).length);
69
- return files;
70
- };
71
-
72
- //#endregion
73
- //#region src/plugin.ts
74
- const CJS_SHIM_PREFIX = "\0cjs-shim:";
75
- const plugin = (args) => {
76
- const packageJsonCache = /* @__PURE__ */ new Map();
77
- const shimMeta = /* @__PURE__ */ new Map();
78
- const tracedPaths = /* @__PURE__ */ new Set();
79
- const isBareImport = (id) => {
80
- return !id.startsWith(".") && !id.startsWith("/") && !/^[a-z][a-z0-9+.-]*:/i.test(id);
81
- };
82
- /**
83
- * Read and cache package.json contents
84
- */
85
- const getPackageJson = async (pkgPath) => {
86
- if (packageJsonCache.has(pkgPath)) return packageJsonCache.get(pkgPath);
87
- try {
88
- const contents = await readFile(pkgPath, "utf-8");
89
- const parsed = JSON.parse(contents);
90
- packageJsonCache.set(pkgPath, parsed);
91
- return parsed;
92
- } catch {
93
- packageJsonCache.set(pkgPath, null);
94
- return null;
95
- }
96
- };
97
- /**
98
- * Determine if a resolved module is CommonJS based on package.json exports
99
- */
100
- const isCommonJS = async (bareImport, resolvedPath, resolvedInfo) => {
101
- const ext = extname(resolvedPath);
102
- if (ext === ".cjs") return true;
103
- if (ext === ".mjs") return false;
104
- if (ext === ".js" || ext === ".ts") {
105
- const pkgJsonPath = resolvedInfo.packageJsonPath;
106
- if (!pkgJsonPath) return true;
107
- const pkgJson = await getPackageJson(pkgJsonPath);
108
- if (!pkgJson) return true;
109
- const pkgDir = dirname(pkgJsonPath);
110
- const relativePath = resolvedPath.startsWith(pkgDir) ? resolvedPath.slice(pkgDir.length + 1).replace(/\\/g, "/") : null;
111
- if (!relativePath) return pkgJson.type !== "module";
112
- const pkgName = pkgJson.name || "";
113
- const subpath = bareImport.startsWith(pkgName) ? `.${bareImport.slice(pkgName.length)}` || "." : ".";
114
- try {
115
- if (exports(pkgJson, subpath, {
116
- require: false,
117
- conditions: ["node", "import"]
118
- })?.some((p) => p === relativePath || p === `./${relativePath}`)) return false;
119
- if (exports(pkgJson, subpath, {
120
- require: true,
121
- conditions: ["node", "require"]
122
- })?.some((p) => p === relativePath || p === `./${relativePath}`)) return true;
123
- } catch (err) {
124
- console.warn("Export resolution failed::", err);
125
- }
126
- if (pkgJson.module) return false;
127
- return pkgJson.type !== "module";
128
- }
129
- return true;
130
- };
131
- const isLocalImport = (id) => {
132
- if (id.startsWith("node:")) return false;
133
- if (id.includes("node_modules")) return false;
134
- return true;
135
- };
136
- return {
137
- name: "cervel",
138
- resolveId: {
139
- order: "pre",
140
- async handler(id, importer, rOpts) {
141
- if (id.startsWith(CJS_SHIM_PREFIX)) return {
142
- id,
143
- external: false
144
- };
145
- const resolved = await this.resolve(id, importer, rOpts);
146
- if (builtinModules.includes(id)) return {
147
- id: `node:${id}`,
148
- external: true
149
- };
150
- if (resolved?.id && isLocalImport(resolved.id)) tracedPaths.add(resolved.id);
151
- if (importer?.startsWith(CJS_SHIM_PREFIX) && isBareImport(id)) return {
152
- id,
153
- external: true
154
- };
155
- if (importer && isBareImport(id) && resolved?.id?.includes("node_modules")) {
156
- if (args.shimBareImports) {
157
- if (await isCommonJS(id, resolved.id, resolved)) {
158
- const importerPkgJsonPath = (await this.resolve(importer))?.packageJsonPath;
159
- if (importerPkgJsonPath) {
160
- const importerPkgDir = relative(args.repoRootPath, dirname(importerPkgJsonPath));
161
- const shimId$1 = `${CJS_SHIM_PREFIX}${importerPkgDir.replace(/\//g, "_")}_${id.replace(/\//g, "_")}`;
162
- shimMeta.set(shimId$1, {
163
- pkgDir: importerPkgDir,
164
- pkgName: id
165
- });
166
- return {
167
- id: shimId$1,
168
- external: false
169
- };
170
- }
171
- const shimId = `${CJS_SHIM_PREFIX}${id.replace(/\//g, "_")}`;
172
- shimMeta.set(shimId, {
173
- pkgDir: "",
174
- pkgName: id
175
- });
176
- return {
177
- id: shimId,
178
- external: false
179
- };
180
- }
181
- }
182
- return {
183
- external: true,
184
- id
185
- };
186
- }
187
- if (importer && isBareImport(id)) return resolved;
188
- return {
189
- external: true,
190
- ...resolved,
191
- id: resolved?.id || id
192
- };
193
- }
194
- },
195
- load: { async handler(id) {
196
- if (id.startsWith(CJS_SHIM_PREFIX)) {
197
- const meta = shimMeta.get(id);
198
- if (!meta) return { code: `module.exports = require('${id.slice(10)}');` };
199
- const { pkgDir, pkgName } = meta;
200
- if (pkgDir) return { code: `
201
- import { createRequire } from 'node:module';
202
- import { fileURLToPath } from 'node:url';
203
- import { dirname, join } from 'node:path';
204
-
205
- const requireFromContext = createRequire(join(dirname(fileURLToPath(import.meta.url)), '${join("..", pkgDir, "package.json")}'));
206
- module.exports = requireFromContext('${pkgName}');
207
- `.trim() };
208
- return { code: `module.exports = require('${pkgName}');` };
209
- }
210
- return null;
211
- } },
212
- writeBundle: {
213
- order: "post",
214
- async handler() {
215
- const files = await nodeFileTrace$1({
216
- outDir: args.outDir,
217
- tracedPaths: Array.from(tracedPaths),
218
- repoRootPath: args.repoRootPath,
219
- workPath: args.workPath,
220
- context: args.context,
221
- keepTracedPaths: false
222
- });
223
- args.context.files = files;
224
- }
225
- }
226
- };
227
- };
228
-
229
- //#endregion
230
- //#region src/rolldown.ts
231
- const __dirname__filenameShim = `
232
- import { createRequire as __createRequire } from 'node:module';
233
- import { fileURLToPath as __fileURLToPath } from 'node:url';
234
- import { dirname as __dirname_ } from 'node:path';
235
- const require = __createRequire(import.meta.url);
236
- const __filename = __fileURLToPath(import.meta.url);
237
- const __dirname = __dirname_(__filename);
238
- `.trim();
239
- const rolldown = async (args) => {
240
- const entrypointPath = join(args.workPath, args.entrypoint);
241
- const outputDir = join(args.workPath, args.out);
242
- const extension = extname(args.entrypoint);
243
- const extensionMap = {
244
- ".ts": {
245
- format: "auto",
246
- extension: "js"
247
- },
248
- ".mts": {
249
- format: "esm",
250
- extension: "mjs"
251
- },
252
- ".cts": {
253
- format: "cjs",
254
- extension: "cjs"
255
- },
256
- ".cjs": {
257
- format: "cjs",
258
- extension: "cjs"
259
- },
260
- ".js": {
261
- format: "auto",
262
- extension: "js"
263
- },
264
- ".mjs": {
265
- format: "esm",
266
- extension: "mjs"
267
- }
268
- };
269
- const extensionInfo = extensionMap[extension] || extensionMap[".js"];
270
- let resolvedFormat = extensionInfo.format === "auto" ? void 0 : extensionInfo.format;
271
- const packageJsonPath = join(args.workPath, "package.json");
272
- const external = [];
273
- let pkg = {};
274
- if (existsSync(packageJsonPath)) {
275
- const source = await readFile(packageJsonPath, "utf8");
276
- try {
277
- pkg = JSON.parse(source.toString());
278
- } catch (_e) {
279
- pkg = {};
280
- }
281
- if (extensionInfo.format === "auto") if (pkg.type === "module") resolvedFormat = "esm";
282
- else resolvedFormat = "cjs";
283
- for (const dependency of Object.keys(pkg.dependencies || {})) external.push(dependency);
284
- for (const dependency of Object.keys(pkg.devDependencies || {})) external.push(dependency);
285
- for (const dependency of Object.keys(pkg.peerDependencies || {})) external.push(dependency);
286
- for (const dependency of Object.keys(pkg.optionalDependencies || {})) external.push(dependency);
287
- }
288
- const resolvedExtension = resolvedFormat === "esm" ? "mjs" : "cjs";
289
- const context = { files: {} };
290
- const out = await build({
291
- input: entrypointPath,
292
- cwd: args.workPath,
293
- platform: "node",
294
- tsconfig: true,
295
- plugins: [plugin({
296
- repoRootPath: args.repoRootPath,
297
- outDir: outputDir,
298
- workPath: args.workPath,
299
- shimBareImports: resolvedFormat === "esm",
300
- context
301
- })],
302
- output: {
303
- cleanDir: true,
304
- dir: outputDir,
305
- format: resolvedFormat,
306
- entryFileNames: `[name].${resolvedExtension}`,
307
- preserveModules: true,
308
- preserveModulesRoot: args.repoRootPath,
309
- sourcemap: false,
310
- banner: resolvedFormat === "esm" ? __dirname__filenameShim : void 0
311
- }
312
- });
313
- let handler = null;
314
- for (const entry of out.output) if (entry.type === "chunk") {
315
- if (entry.isEntry) handler = entry.fileName;
316
- }
317
- if (typeof handler !== "string") throw new Error(`Unable to resolve module for ${args.entrypoint}`);
318
- const cleanup = async () => {
319
- await rm(outputDir, {
320
- recursive: true,
321
- force: true
322
- });
323
- };
324
- return {
325
- result: {
326
- handler,
327
- outputDir,
328
- outputFiles: context.files
329
- },
330
- cleanup
331
- };
332
- };
333
-
334
- //#endregion
335
- //#region src/utils.ts
336
- const noColor = globalThis.process?.env?.NO_COLOR === "1" || globalThis.process?.env?.TERM === "dumb";
337
- const resets = {
338
- 1: 22,
339
- 31: 39,
340
- 32: 39,
341
- 33: 39,
342
- 34: 39,
343
- 35: 39,
344
- 36: 39,
345
- 90: 39
346
- };
347
- const _c = (c) => (text) => {
348
- if (noColor) return text;
349
- return `\u001B[${c}m${text}\u001B[${resets[c] ?? 0}m`;
350
- };
351
- const Colors = {
352
- bold: _c(1),
353
- red: _c(31),
354
- green: _c(32),
355
- yellow: _c(33),
356
- blue: _c(34),
357
- magenta: _c(35),
358
- cyan: _c(36),
359
- gray: _c(90),
360
- url: (title, url) => noColor ? `[${title}](${url})` : `\u001B]8;;${url}\u001B\\${title}\u001B]8;;\u001B\\`
361
- };
362
-
363
- //#endregion
364
- //#region src/typescript.ts
365
- const require_ = createRequire$1(import.meta.url);
366
- const typescript = (args) => {
367
- const extension = extname$1(args.entrypoint);
368
- if (![
369
- ".ts",
370
- ".mts",
371
- ".cts"
372
- ].includes(extension)) return;
373
- const tscPath = resolveTscPath(args);
374
- if (!tscPath) {
375
- console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck skipped ${Colors.gray("(TypeScript not found)")}`));
376
- return null;
377
- }
378
- return doTypeCheck(args, tscPath);
379
- };
380
- async function doTypeCheck(args, tscPath) {
381
- let stdout = "";
382
- let stderr = "";
383
- /**
384
- * This might be subject to change.
385
- * - if no tscPath, skip typecheck
386
- * - if tsconfig, provide the tsconfig path
387
- * - else provide the entrypoint path
388
- */
389
- const tscArgs = [
390
- tscPath,
391
- "--noEmit",
392
- "--pretty",
393
- "--allowJs",
394
- "--esModuleInterop",
395
- "--skipLibCheck"
396
- ];
397
- const tsconfig = await findNearestTsconfig(args.workPath);
398
- if (tsconfig) tscArgs.push("--project", tsconfig);
399
- else tscArgs.push(args.entrypoint);
400
- const child = spawn(process.execPath, tscArgs, {
401
- cwd: args.workPath,
402
- stdio: [
403
- "ignore",
404
- "pipe",
405
- "pipe"
406
- ]
407
- });
408
- child.stdout?.on("data", (data) => {
409
- stdout += data.toString();
410
- });
411
- child.stderr?.on("data", (data) => {
412
- stderr += data.toString();
413
- });
414
- await new Promise((resolve$1, reject) => {
415
- child.on("close", (code) => {
416
- if (code === 0) {
417
- console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck complete`));
418
- resolve$1();
419
- } else {
420
- const output = stdout || stderr;
421
- if (output) {
422
- console.error("\nTypeScript type check failed:\n");
423
- console.error(output);
424
- }
425
- reject(/* @__PURE__ */ new Error("TypeScript type check failed"));
426
- }
427
- });
428
- child.on("error", (err) => {
429
- reject(err);
430
- });
431
- });
432
- }
433
- const resolveTscPath = (args) => {
434
- try {
435
- return require_.resolve("typescript/bin/tsc", { paths: [args.workPath] });
436
- } catch (e) {
437
- return null;
438
- }
439
- };
440
- const findNearestTsconfig = async (workPath) => {
441
- const tsconfigPath = join$1(workPath, "tsconfig.json");
442
- if (existsSync$1(tsconfigPath)) return tsconfigPath;
443
- if (workPath === "/") return;
444
- return findNearestTsconfig(join$1(workPath, ".."));
445
- };
446
-
447
- //#endregion
448
- //#region src/find-entrypoint.ts
449
- const frameworks = [
450
- "express",
451
- "hono",
452
- "elysia",
453
- "fastify",
454
- "@nestjs/core",
455
- "h3"
456
- ];
457
- const entrypointFilenames = [
458
- "app",
459
- "index",
460
- "server",
461
- "main"
462
- ];
463
- const entrypointExtensions = [
464
- "js",
465
- "cjs",
466
- "mjs",
467
- "ts",
468
- "cts",
469
- "mts"
470
- ];
471
- const entrypoints = entrypointFilenames.flatMap((filename) => entrypointExtensions.map((extension) => `${filename}.${extension}`));
472
- const createFrameworkRegex = (framework) => new RegExp(`(?:from|require|import)\\s*(?:\\(\\s*)?["']${framework}["']\\s*(?:\\))?`, "g");
473
- const findEntrypoint = async (cwd, options) => {
474
- if (options?.ignoreRegex ?? false) {
475
- for (const entrypoint of entrypoints) if (existsSync(join(cwd, entrypoint))) return entrypoint;
476
- for (const entrypoint of entrypoints) if (existsSync(join(cwd, "src", entrypoint))) return join("src", entrypoint);
477
- throw new Error("No entrypoint file found");
478
- }
479
- const packageJson = await readFile(join(cwd, "package.json"), "utf-8");
480
- const packageJsonObject = JSON.parse(packageJson);
481
- const framework = frameworks.find((framework$1) => packageJsonObject.dependencies?.[framework$1]);
482
- if (!framework) {
483
- for (const entrypoint of entrypoints) {
484
- const entrypointPath = join(cwd, entrypoint);
485
- try {
486
- await readFile(entrypointPath, "utf-8");
487
- return entrypoint;
488
- } catch (e) {
489
- continue;
490
- }
491
- }
492
- throw new Error("No entrypoint or framework found");
493
- }
494
- const regex = createFrameworkRegex(framework);
495
- for (const entrypoint of entrypoints) {
496
- const entrypointPath = join(cwd, entrypoint);
497
- try {
498
- const content = await readFile(entrypointPath, "utf-8");
499
- if (regex.test(content)) return entrypoint;
500
- } catch (e) {
501
- continue;
502
- }
503
- }
504
- for (const entrypoint of entrypoints) {
505
- const entrypointPath = join(cwd, "src", entrypoint);
506
- try {
507
- const content = await readFile(entrypointPath, "utf-8");
508
- if (regex.test(content)) return join("src", entrypoint);
509
- } catch (e) {
510
- continue;
511
- }
512
- }
513
- throw new Error("No entrypoint found");
514
- };
515
-
516
- //#endregion
517
- //#region src/index.ts
518
- const require = createRequire(import.meta.url);
519
- const build$1 = async (args) => {
520
- const entrypoint = args.entrypoint || await findEntrypoint(args.workPath);
521
- const tsPromise = typescript({
522
- entrypoint,
523
- workPath: args.workPath
524
- });
525
- const rolldownResult = await rolldown({
526
- entrypoint,
527
- workPath: args.workPath,
528
- repoRootPath: args.repoRootPath,
529
- out: args.out
530
- });
531
- await writeFile(join(args.workPath, args.out, ".cervel.json"), JSON.stringify({ handler: rolldownResult.result.handler }, null, 2));
532
- console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Build complete`));
533
- const typecheckComplete = true;
534
- const result = tsPromise ? await Promise.race([tsPromise.then(() => typecheckComplete), Promise.resolve(false)]) : true;
535
- if (tsPromise && !result) console.log(Colors.gray(`${Colors.bold(Colors.gray("*"))} Waiting for typecheck...`));
536
- return {
537
- rolldownResult: rolldownResult.result,
538
- tsPromise
539
- };
540
- };
541
- const serve = async (args) => {
542
- const entrypoint = await findEntrypoint(args.workPath);
543
- const srvxBin = join(require.resolve("srvx"), "..", "..", "..", "bin", "srvx.mjs");
544
- const tsxBin = require.resolve("tsx");
545
- const restArgs = Object.entries(args.rest).filter(([, value]) => value !== void 0 && value !== false).map(([key, value]) => typeof value === "boolean" ? `--${key}` : `--${key}=${value}`);
546
- if (!args.rest.import) restArgs.push("--import", tsxBin);
547
- await execa("npx", [
548
- srvxBin,
549
- ...restArgs,
550
- entrypoint
551
- ], {
552
- cwd: args.workPath,
553
- stdio: "inherit"
554
- });
555
- };
556
- const srvxOptions = {
557
- help: {
558
- type: "boolean",
559
- short: "h"
560
- },
561
- version: {
562
- type: "boolean",
563
- short: "v"
564
- },
565
- prod: { type: "boolean" },
566
- port: {
567
- type: "string",
568
- short: "p"
569
- },
570
- host: {
571
- type: "string",
572
- short: "H"
573
- },
574
- static: {
575
- type: "string",
576
- short: "s"
577
- },
578
- import: { type: "string" },
579
- tls: { type: "boolean" },
580
- cert: { type: "string" },
581
- key: { type: "string" }
582
- };
583
-
584
- //#endregion
585
4
  //#region src/cli.ts
586
5
  const main = async () => {
587
6
  const options = parseArgs$1(process.argv.slice(2));
@@ -589,15 +8,13 @@ const main = async () => {
589
8
  const [command, entrypoint] = options.positionals;
590
9
  const workPath = cwd;
591
10
  const repoRootPath = cwd;
592
- if (command === "build") {
593
- const { tsPromise } = await build$1({
594
- workPath,
595
- repoRootPath,
596
- out,
597
- entrypoint
598
- });
599
- await tsPromise;
600
- } else await serve({
11
+ if (command === "build") await build({
12
+ workPath,
13
+ repoRootPath,
14
+ out,
15
+ entrypoint
16
+ });
17
+ else await serve({
601
18
  workPath,
602
19
  rest
603
20
  });
package/dist/index.d.mts CHANGED
@@ -1,60 +1,2 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
- import { BuildOptions, Files } from "@vercel/build-utils";
3
- import * as _vercel_build_utils_dist_types_js0 from "@vercel/build-utils/dist/types.js";
4
- import { ParseArgsConfig } from "node:util";
5
-
6
- //#region src/find-entrypoint.d.ts
7
- declare const findEntrypoint: (cwd: string, options?: {
8
- ignoreRegex?: boolean;
9
- }) => Promise<string>;
10
- //#endregion
11
- //#region src/types.d.ts
12
- /**
13
- * Core path options derived from BuildOptions.
14
- * - workPath: the workspace/project directory (where package.json is)
15
- * - repoRootPath: the root of the monorepo/repo
16
- */
17
- type PathOptions = Pick<BuildOptions, 'workPath' | 'repoRootPath'>;
18
- /**
19
- * Options for the cervel build function.
20
- */
21
- type CervelBuildOptions = PathOptions & {
22
- entrypoint?: string;
23
- out: string;
24
- };
25
- /**
26
- * Options for the cervel serve function.
27
- */
28
- type CervelServeOptions = Pick<BuildOptions, 'workPath'> & {
29
- rest: Record<string, string | boolean | undefined>;
30
- };
31
- /**
32
- * Options for node file tracing.
33
- */
34
- type NodeFileTraceOptions = PathOptions & {
35
- keepTracedPaths: boolean;
36
- outDir: string;
37
- tracedPaths: string[];
38
- context: {
39
- files: Files;
40
- };
41
- };
42
- //#endregion
43
- //#region src/node-file-trace.d.ts
44
- declare const nodeFileTrace: (args: NodeFileTraceOptions) => Promise<Files>;
45
- //#endregion
46
- //#region src/index.d.ts
47
- type ParseArgsOptionsConfig = NonNullable<ParseArgsConfig['options']>;
48
- declare const getBuildSummary: (outputDir: string) => Promise<any>;
49
- declare const build: (args: CervelBuildOptions) => Promise<{
50
- rolldownResult: {
51
- handler: string;
52
- outputDir: string;
53
- outputFiles: _vercel_build_utils_dist_types_js0.Files;
54
- };
55
- tsPromise: Promise<void> | null | undefined;
56
- }>;
57
- declare const serve: (args: CervelServeOptions) => Promise<void>;
58
- declare const srvxOptions: ParseArgsOptionsConfig;
59
- //#endregion
1
+ import { CervelBuildOptions, CervelServeOptions, PathOptions, cervelBuild as build, cervelServe as serve, findEntrypoint, getBuildSummary, nodeFileTrace, srvxOptions } from "@vercel/backends";
60
2
  export { type CervelBuildOptions, type CervelServeOptions, type PathOptions, build, findEntrypoint, getBuildSummary, nodeFileTrace, serve, srvxOptions };
package/dist/index.mjs CHANGED
@@ -1,588 +1,3 @@
1
- import { builtinModules, createRequire } from "node:module";
2
- import { existsSync } from "node:fs";
3
- import { lstat, readFile, rm } from "node:fs/promises";
4
- import { dirname, extname, join, relative } from "node:path";
5
- import { build as build$1 } from "rolldown";
6
- import { exports } from "resolve.exports";
7
- import { isNativeError } from "node:util/types";
8
- import { FileFsRef, debug, glob } from "@vercel/build-utils";
9
- import { nodeFileTrace as nodeFileTrace$1, resolve } from "@vercel/nft";
10
- import { transform } from "oxc-transform";
11
- import { createRequire as createRequire$1 } from "module";
12
- import { spawn } from "child_process";
13
- import { extname as extname$1, join as join$1 } from "path";
14
- import { existsSync as existsSync$1 } from "fs";
15
- import execa from "execa";
16
- import { readFile as readFile$1, writeFile } from "fs/promises";
1
+ import { cervelBuild as build, cervelServe as serve, findEntrypoint, getBuildSummary, nodeFileTrace, srvxOptions } from "@vercel/backends";
17
2
 
18
- //#region src/node-file-trace.ts
19
- const nodeFileTrace = async (args) => {
20
- const files = {};
21
- const { tracedPaths } = args;
22
- const compiledSourceFiles = await glob("**/*", {
23
- cwd: args.outDir,
24
- follow: true,
25
- includeDirectories: true
26
- });
27
- for (const file of Object.keys(compiledSourceFiles)) files[file] = compiledSourceFiles[file];
28
- /**
29
- * While we're not using NFT to process source code, we are using it
30
- * to tree shake node deps, and include any fs reads for files that are
31
- * not part of the traced paths or compiled source files.
32
- * Most of this is identical to the `@vercel/node` implementation
33
- */
34
- const result = await nodeFileTrace$1(Array.from(tracedPaths), {
35
- base: args.repoRootPath,
36
- processCwd: args.workPath,
37
- ts: true,
38
- mixedModules: true,
39
- async resolve(id, parent, job, cjsResolve) {
40
- return resolve(id, parent, job, cjsResolve);
41
- },
42
- async readFile(fsPath) {
43
- try {
44
- let source = await readFile(fsPath);
45
- if (fsPath.endsWith(".ts") && !fsPath.endsWith(".d.ts") || fsPath.endsWith(".tsx") || fsPath.endsWith(".mts") || fsPath.endsWith(".cts")) source = (await transform(fsPath, source.toString())).code;
46
- return source;
47
- } catch (error) {
48
- if (isNativeError(error) && "code" in error && (error.code === "ENOENT" || error.code === "EISDIR")) return null;
49
- throw error;
50
- }
51
- }
52
- });
53
- if (!args.keepTracedPaths) for (const file of tracedPaths) {
54
- const relativeFile = relative(args.repoRootPath, file);
55
- result.fileList.delete(relativeFile);
56
- }
57
- debug("NFT traced files count:", result.fileList.size);
58
- for (const file of result.fileList) {
59
- const absolutePath = join(args.repoRootPath, file);
60
- const stats = await lstat(absolutePath);
61
- const outputPath = file;
62
- if (stats.isSymbolicLink() || stats.isFile()) files[outputPath] = new FileFsRef({
63
- fsPath: absolutePath,
64
- mode: stats.mode
65
- });
66
- }
67
- debug("Total files in context:", Object.keys(files).length);
68
- return files;
69
- };
70
-
71
- //#endregion
72
- //#region src/plugin.ts
73
- const CJS_SHIM_PREFIX = "\0cjs-shim:";
74
- const plugin = (args) => {
75
- const packageJsonCache = /* @__PURE__ */ new Map();
76
- const shimMeta = /* @__PURE__ */ new Map();
77
- const tracedPaths = /* @__PURE__ */ new Set();
78
- const isBareImport = (id) => {
79
- return !id.startsWith(".") && !id.startsWith("/") && !/^[a-z][a-z0-9+.-]*:/i.test(id);
80
- };
81
- /**
82
- * Read and cache package.json contents
83
- */
84
- const getPackageJson = async (pkgPath) => {
85
- if (packageJsonCache.has(pkgPath)) return packageJsonCache.get(pkgPath);
86
- try {
87
- const contents = await readFile(pkgPath, "utf-8");
88
- const parsed = JSON.parse(contents);
89
- packageJsonCache.set(pkgPath, parsed);
90
- return parsed;
91
- } catch {
92
- packageJsonCache.set(pkgPath, null);
93
- return null;
94
- }
95
- };
96
- /**
97
- * Determine if a resolved module is CommonJS based on package.json exports
98
- */
99
- const isCommonJS = async (bareImport, resolvedPath, resolvedInfo) => {
100
- const ext = extname(resolvedPath);
101
- if (ext === ".cjs") return true;
102
- if (ext === ".mjs") return false;
103
- if (ext === ".js" || ext === ".ts") {
104
- const pkgJsonPath = resolvedInfo.packageJsonPath;
105
- if (!pkgJsonPath) return true;
106
- const pkgJson = await getPackageJson(pkgJsonPath);
107
- if (!pkgJson) return true;
108
- const pkgDir = dirname(pkgJsonPath);
109
- const relativePath = resolvedPath.startsWith(pkgDir) ? resolvedPath.slice(pkgDir.length + 1).replace(/\\/g, "/") : null;
110
- if (!relativePath) return pkgJson.type !== "module";
111
- const pkgName = pkgJson.name || "";
112
- const subpath = bareImport.startsWith(pkgName) ? `.${bareImport.slice(pkgName.length)}` || "." : ".";
113
- try {
114
- if (exports(pkgJson, subpath, {
115
- require: false,
116
- conditions: ["node", "import"]
117
- })?.some((p) => p === relativePath || p === `./${relativePath}`)) return false;
118
- if (exports(pkgJson, subpath, {
119
- require: true,
120
- conditions: ["node", "require"]
121
- })?.some((p) => p === relativePath || p === `./${relativePath}`)) return true;
122
- } catch (err) {
123
- console.warn("Export resolution failed::", err);
124
- }
125
- if (pkgJson.module) return false;
126
- return pkgJson.type !== "module";
127
- }
128
- return true;
129
- };
130
- const isLocalImport = (id) => {
131
- if (id.startsWith("node:")) return false;
132
- if (id.includes("node_modules")) return false;
133
- return true;
134
- };
135
- return {
136
- name: "cervel",
137
- resolveId: {
138
- order: "pre",
139
- async handler(id, importer, rOpts) {
140
- if (id.startsWith(CJS_SHIM_PREFIX)) return {
141
- id,
142
- external: false
143
- };
144
- const resolved = await this.resolve(id, importer, rOpts);
145
- if (builtinModules.includes(id)) return {
146
- id: `node:${id}`,
147
- external: true
148
- };
149
- if (resolved?.id && isLocalImport(resolved.id)) tracedPaths.add(resolved.id);
150
- if (importer?.startsWith(CJS_SHIM_PREFIX) && isBareImport(id)) return {
151
- id,
152
- external: true
153
- };
154
- if (importer && isBareImport(id) && resolved?.id?.includes("node_modules")) {
155
- if (args.shimBareImports) {
156
- if (await isCommonJS(id, resolved.id, resolved)) {
157
- const importerPkgJsonPath = (await this.resolve(importer))?.packageJsonPath;
158
- if (importerPkgJsonPath) {
159
- const importerPkgDir = relative(args.repoRootPath, dirname(importerPkgJsonPath));
160
- const shimId$1 = `${CJS_SHIM_PREFIX}${importerPkgDir.replace(/\//g, "_")}_${id.replace(/\//g, "_")}`;
161
- shimMeta.set(shimId$1, {
162
- pkgDir: importerPkgDir,
163
- pkgName: id
164
- });
165
- return {
166
- id: shimId$1,
167
- external: false
168
- };
169
- }
170
- const shimId = `${CJS_SHIM_PREFIX}${id.replace(/\//g, "_")}`;
171
- shimMeta.set(shimId, {
172
- pkgDir: "",
173
- pkgName: id
174
- });
175
- return {
176
- id: shimId,
177
- external: false
178
- };
179
- }
180
- }
181
- return {
182
- external: true,
183
- id
184
- };
185
- }
186
- if (importer && isBareImport(id)) return resolved;
187
- return {
188
- external: true,
189
- ...resolved,
190
- id: resolved?.id || id
191
- };
192
- }
193
- },
194
- load: { async handler(id) {
195
- if (id.startsWith(CJS_SHIM_PREFIX)) {
196
- const meta = shimMeta.get(id);
197
- if (!meta) return { code: `module.exports = require('${id.slice(10)}');` };
198
- const { pkgDir, pkgName } = meta;
199
- if (pkgDir) return { code: `
200
- import { createRequire } from 'node:module';
201
- import { fileURLToPath } from 'node:url';
202
- import { dirname, join } from 'node:path';
203
-
204
- const requireFromContext = createRequire(join(dirname(fileURLToPath(import.meta.url)), '${join("..", pkgDir, "package.json")}'));
205
- module.exports = requireFromContext('${pkgName}');
206
- `.trim() };
207
- return { code: `module.exports = require('${pkgName}');` };
208
- }
209
- return null;
210
- } },
211
- writeBundle: {
212
- order: "post",
213
- async handler() {
214
- const files = await nodeFileTrace({
215
- outDir: args.outDir,
216
- tracedPaths: Array.from(tracedPaths),
217
- repoRootPath: args.repoRootPath,
218
- workPath: args.workPath,
219
- context: args.context,
220
- keepTracedPaths: false
221
- });
222
- args.context.files = files;
223
- }
224
- }
225
- };
226
- };
227
-
228
- //#endregion
229
- //#region src/rolldown.ts
230
- const __dirname__filenameShim = `
231
- import { createRequire as __createRequire } from 'node:module';
232
- import { fileURLToPath as __fileURLToPath } from 'node:url';
233
- import { dirname as __dirname_ } from 'node:path';
234
- const require = __createRequire(import.meta.url);
235
- const __filename = __fileURLToPath(import.meta.url);
236
- const __dirname = __dirname_(__filename);
237
- `.trim();
238
- const rolldown = async (args) => {
239
- const entrypointPath = join(args.workPath, args.entrypoint);
240
- const outputDir = join(args.workPath, args.out);
241
- const extension = extname(args.entrypoint);
242
- const extensionMap = {
243
- ".ts": {
244
- format: "auto",
245
- extension: "js"
246
- },
247
- ".mts": {
248
- format: "esm",
249
- extension: "mjs"
250
- },
251
- ".cts": {
252
- format: "cjs",
253
- extension: "cjs"
254
- },
255
- ".cjs": {
256
- format: "cjs",
257
- extension: "cjs"
258
- },
259
- ".js": {
260
- format: "auto",
261
- extension: "js"
262
- },
263
- ".mjs": {
264
- format: "esm",
265
- extension: "mjs"
266
- }
267
- };
268
- const extensionInfo = extensionMap[extension] || extensionMap[".js"];
269
- let resolvedFormat = extensionInfo.format === "auto" ? void 0 : extensionInfo.format;
270
- const packageJsonPath = join(args.workPath, "package.json");
271
- const external = [];
272
- let pkg = {};
273
- if (existsSync(packageJsonPath)) {
274
- const source = await readFile(packageJsonPath, "utf8");
275
- try {
276
- pkg = JSON.parse(source.toString());
277
- } catch (_e) {
278
- pkg = {};
279
- }
280
- if (extensionInfo.format === "auto") if (pkg.type === "module") resolvedFormat = "esm";
281
- else resolvedFormat = "cjs";
282
- for (const dependency of Object.keys(pkg.dependencies || {})) external.push(dependency);
283
- for (const dependency of Object.keys(pkg.devDependencies || {})) external.push(dependency);
284
- for (const dependency of Object.keys(pkg.peerDependencies || {})) external.push(dependency);
285
- for (const dependency of Object.keys(pkg.optionalDependencies || {})) external.push(dependency);
286
- }
287
- const resolvedExtension = resolvedFormat === "esm" ? "mjs" : "cjs";
288
- const context = { files: {} };
289
- const out = await build$1({
290
- input: entrypointPath,
291
- cwd: args.workPath,
292
- platform: "node",
293
- tsconfig: true,
294
- plugins: [plugin({
295
- repoRootPath: args.repoRootPath,
296
- outDir: outputDir,
297
- workPath: args.workPath,
298
- shimBareImports: resolvedFormat === "esm",
299
- context
300
- })],
301
- output: {
302
- cleanDir: true,
303
- dir: outputDir,
304
- format: resolvedFormat,
305
- entryFileNames: `[name].${resolvedExtension}`,
306
- preserveModules: true,
307
- preserveModulesRoot: args.repoRootPath,
308
- sourcemap: false,
309
- banner: resolvedFormat === "esm" ? __dirname__filenameShim : void 0
310
- }
311
- });
312
- let handler = null;
313
- for (const entry of out.output) if (entry.type === "chunk") {
314
- if (entry.isEntry) handler = entry.fileName;
315
- }
316
- if (typeof handler !== "string") throw new Error(`Unable to resolve module for ${args.entrypoint}`);
317
- const cleanup = async () => {
318
- await rm(outputDir, {
319
- recursive: true,
320
- force: true
321
- });
322
- };
323
- return {
324
- result: {
325
- handler,
326
- outputDir,
327
- outputFiles: context.files
328
- },
329
- cleanup
330
- };
331
- };
332
-
333
- //#endregion
334
- //#region src/utils.ts
335
- const noColor = globalThis.process?.env?.NO_COLOR === "1" || globalThis.process?.env?.TERM === "dumb";
336
- const resets = {
337
- 1: 22,
338
- 31: 39,
339
- 32: 39,
340
- 33: 39,
341
- 34: 39,
342
- 35: 39,
343
- 36: 39,
344
- 90: 39
345
- };
346
- const _c = (c) => (text) => {
347
- if (noColor) return text;
348
- return `\u001B[${c}m${text}\u001B[${resets[c] ?? 0}m`;
349
- };
350
- const Colors = {
351
- bold: _c(1),
352
- red: _c(31),
353
- green: _c(32),
354
- yellow: _c(33),
355
- blue: _c(34),
356
- magenta: _c(35),
357
- cyan: _c(36),
358
- gray: _c(90),
359
- url: (title, url) => noColor ? `[${title}](${url})` : `\u001B]8;;${url}\u001B\\${title}\u001B]8;;\u001B\\`
360
- };
361
-
362
- //#endregion
363
- //#region src/typescript.ts
364
- const require_ = createRequire$1(import.meta.url);
365
- const typescript = (args) => {
366
- const extension = extname$1(args.entrypoint);
367
- if (![
368
- ".ts",
369
- ".mts",
370
- ".cts"
371
- ].includes(extension)) return;
372
- const tscPath = resolveTscPath(args);
373
- if (!tscPath) {
374
- console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck skipped ${Colors.gray("(TypeScript not found)")}`));
375
- return null;
376
- }
377
- return doTypeCheck(args, tscPath);
378
- };
379
- async function doTypeCheck(args, tscPath) {
380
- let stdout = "";
381
- let stderr = "";
382
- /**
383
- * This might be subject to change.
384
- * - if no tscPath, skip typecheck
385
- * - if tsconfig, provide the tsconfig path
386
- * - else provide the entrypoint path
387
- */
388
- const tscArgs = [
389
- tscPath,
390
- "--noEmit",
391
- "--pretty",
392
- "--allowJs",
393
- "--esModuleInterop",
394
- "--skipLibCheck"
395
- ];
396
- const tsconfig = await findNearestTsconfig(args.workPath);
397
- if (tsconfig) tscArgs.push("--project", tsconfig);
398
- else tscArgs.push(args.entrypoint);
399
- const child = spawn(process.execPath, tscArgs, {
400
- cwd: args.workPath,
401
- stdio: [
402
- "ignore",
403
- "pipe",
404
- "pipe"
405
- ]
406
- });
407
- child.stdout?.on("data", (data) => {
408
- stdout += data.toString();
409
- });
410
- child.stderr?.on("data", (data) => {
411
- stderr += data.toString();
412
- });
413
- await new Promise((resolve$1, reject) => {
414
- child.on("close", (code) => {
415
- if (code === 0) {
416
- console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck complete`));
417
- resolve$1();
418
- } else {
419
- const output = stdout || stderr;
420
- if (output) {
421
- console.error("\nTypeScript type check failed:\n");
422
- console.error(output);
423
- }
424
- reject(/* @__PURE__ */ new Error("TypeScript type check failed"));
425
- }
426
- });
427
- child.on("error", (err) => {
428
- reject(err);
429
- });
430
- });
431
- }
432
- const resolveTscPath = (args) => {
433
- try {
434
- return require_.resolve("typescript/bin/tsc", { paths: [args.workPath] });
435
- } catch (e) {
436
- return null;
437
- }
438
- };
439
- const findNearestTsconfig = async (workPath) => {
440
- const tsconfigPath = join$1(workPath, "tsconfig.json");
441
- if (existsSync$1(tsconfigPath)) return tsconfigPath;
442
- if (workPath === "/") return;
443
- return findNearestTsconfig(join$1(workPath, ".."));
444
- };
445
-
446
- //#endregion
447
- //#region src/find-entrypoint.ts
448
- const frameworks = [
449
- "express",
450
- "hono",
451
- "elysia",
452
- "fastify",
453
- "@nestjs/core",
454
- "h3"
455
- ];
456
- const entrypointFilenames = [
457
- "app",
458
- "index",
459
- "server",
460
- "main"
461
- ];
462
- const entrypointExtensions = [
463
- "js",
464
- "cjs",
465
- "mjs",
466
- "ts",
467
- "cts",
468
- "mts"
469
- ];
470
- const entrypoints = entrypointFilenames.flatMap((filename) => entrypointExtensions.map((extension) => `${filename}.${extension}`));
471
- const createFrameworkRegex = (framework) => new RegExp(`(?:from|require|import)\\s*(?:\\(\\s*)?["']${framework}["']\\s*(?:\\))?`, "g");
472
- const findEntrypoint = async (cwd, options) => {
473
- if (options?.ignoreRegex ?? false) {
474
- for (const entrypoint of entrypoints) if (existsSync(join(cwd, entrypoint))) return entrypoint;
475
- for (const entrypoint of entrypoints) if (existsSync(join(cwd, "src", entrypoint))) return join("src", entrypoint);
476
- throw new Error("No entrypoint file found");
477
- }
478
- const packageJson = await readFile(join(cwd, "package.json"), "utf-8");
479
- const packageJsonObject = JSON.parse(packageJson);
480
- const framework = frameworks.find((framework$1) => packageJsonObject.dependencies?.[framework$1]);
481
- if (!framework) {
482
- for (const entrypoint of entrypoints) {
483
- const entrypointPath = join(cwd, entrypoint);
484
- try {
485
- await readFile(entrypointPath, "utf-8");
486
- return entrypoint;
487
- } catch (e) {
488
- continue;
489
- }
490
- }
491
- throw new Error("No entrypoint or framework found");
492
- }
493
- const regex = createFrameworkRegex(framework);
494
- for (const entrypoint of entrypoints) {
495
- const entrypointPath = join(cwd, entrypoint);
496
- try {
497
- const content = await readFile(entrypointPath, "utf-8");
498
- if (regex.test(content)) return entrypoint;
499
- } catch (e) {
500
- continue;
501
- }
502
- }
503
- for (const entrypoint of entrypoints) {
504
- const entrypointPath = join(cwd, "src", entrypoint);
505
- try {
506
- const content = await readFile(entrypointPath, "utf-8");
507
- if (regex.test(content)) return join("src", entrypoint);
508
- } catch (e) {
509
- continue;
510
- }
511
- }
512
- throw new Error("No entrypoint found");
513
- };
514
-
515
- //#endregion
516
- //#region src/index.ts
517
- const require = createRequire(import.meta.url);
518
- const getBuildSummary = async (outputDir) => {
519
- const buildSummary = await readFile$1(join(outputDir, ".cervel.json"), "utf-8");
520
- return JSON.parse(buildSummary);
521
- };
522
- const build = async (args) => {
523
- const entrypoint = args.entrypoint || await findEntrypoint(args.workPath);
524
- const tsPromise = typescript({
525
- entrypoint,
526
- workPath: args.workPath
527
- });
528
- const rolldownResult = await rolldown({
529
- entrypoint,
530
- workPath: args.workPath,
531
- repoRootPath: args.repoRootPath,
532
- out: args.out
533
- });
534
- await writeFile(join(args.workPath, args.out, ".cervel.json"), JSON.stringify({ handler: rolldownResult.result.handler }, null, 2));
535
- console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Build complete`));
536
- const typecheckComplete = true;
537
- const result = tsPromise ? await Promise.race([tsPromise.then(() => typecheckComplete), Promise.resolve(false)]) : true;
538
- if (tsPromise && !result) console.log(Colors.gray(`${Colors.bold(Colors.gray("*"))} Waiting for typecheck...`));
539
- return {
540
- rolldownResult: rolldownResult.result,
541
- tsPromise
542
- };
543
- };
544
- const serve = async (args) => {
545
- const entrypoint = await findEntrypoint(args.workPath);
546
- const srvxBin = join(require.resolve("srvx"), "..", "..", "..", "bin", "srvx.mjs");
547
- const tsxBin = require.resolve("tsx");
548
- const restArgs = Object.entries(args.rest).filter(([, value]) => value !== void 0 && value !== false).map(([key, value]) => typeof value === "boolean" ? `--${key}` : `--${key}=${value}`);
549
- if (!args.rest.import) restArgs.push("--import", tsxBin);
550
- await execa("npx", [
551
- srvxBin,
552
- ...restArgs,
553
- entrypoint
554
- ], {
555
- cwd: args.workPath,
556
- stdio: "inherit"
557
- });
558
- };
559
- const srvxOptions = {
560
- help: {
561
- type: "boolean",
562
- short: "h"
563
- },
564
- version: {
565
- type: "boolean",
566
- short: "v"
567
- },
568
- prod: { type: "boolean" },
569
- port: {
570
- type: "string",
571
- short: "p"
572
- },
573
- host: {
574
- type: "string",
575
- short: "H"
576
- },
577
- static: {
578
- type: "string",
579
- short: "s"
580
- },
581
- import: { type: "string" },
582
- tls: { type: "boolean" },
583
- cert: { type: "string" },
584
- key: { type: "string" }
585
- };
586
-
587
- //#endregion
588
3
  export { build, findEntrypoint, getBuildSummary, nodeFileTrace, serve, srvxOptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/cervel",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://vercel.com/docs",
6
6
  "publishConfig": {
@@ -28,15 +28,7 @@
28
28
  "dist"
29
29
  ],
30
30
  "dependencies": {
31
- "@vercel/nft": "1.3.0",
32
- "execa": "3.2.0",
33
- "nf3": "0.3.6",
34
- "oxc-transform": "0.111.0",
35
- "resolve.exports": "2.0.3",
36
- "rolldown": "1.0.0-rc.1",
37
- "srvx": "0.8.9",
38
- "tsx": "4.21.0",
39
- "@vercel/build-utils": "13.2.16"
31
+ "@vercel/backends": "0.0.26"
40
32
  },
41
33
  "peerDependencies": {
42
34
  "typescript": "^4.0.0 || ^5.0.0"