vite-plugin-vercel 0.3.7 → 2.0.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 CHANGED
@@ -1,28 +1,27 @@
1
1
  # vite-plugin-vercel
2
2
 
3
- Vercel adapter for [`vite`](https://vitejs.dev/).
3
+ Vercel adapter for [Vite](https://vitejs.dev/).
4
4
 
5
- Its purpose is to help you bundle your application in `.vercel` folder as supported by
6
- [Vercel API v3](https://vercel.com/docs/build-output-api/v3).
5
+ Bundle your Vite application as supported by [Vercel Output API (v3)](https://vercel.com/docs/build-output-api/v3).
7
6
 
8
7
  ## Features
9
8
 
10
- - [x] [SSG/Static files support](https://vercel.com/docs/build-output-api/v3/primitives#static-files)
9
+ - [x] [SSG/Static files](https://vercel.com/docs/build-output-api/v3/primitives#static-files)
11
10
  - see [`prerender` config](/packages/vercel/src/types.ts#L37)
12
- - [x] [SSR/Serverless functions support](https://vercel.com/docs/build-output-api/v3/primitives#serverless-functions)
11
+ - [x] [SSR/Serverless functions](https://vercel.com/docs/build-output-api/v3/primitives#serverless-functions)
13
12
  - `.[jt]s` files under the `<root>/api` folder of your project are automatically bundled as Serverless functions under `.vercel/output/functions/api/*.func`
14
13
  - see [`additionalEndpoints` config](/packages/vercel/src/types.ts#L62)
15
- - [x] [ISR/Prerender functions support](https://vercel.com/docs/build-output-api/v3/primitives#prerender-functions)
14
+ - [x] [ISR/Prerender functions](https://vercel.com/docs/build-output-api/v3/primitives#prerender-functions)
16
15
  - see [`isr` config](/packages/vercel/src/types.ts#L89). Also see implementation of [vike](/packages/vike-integration/vike.ts) for example
17
- - [x] [Edge functions support](https://vercel.com/docs/build-output-api/v3/primitives#edge-functions)
18
- - [ ] [Images optimization support](https://vercel.com/docs/build-output-api/v3/configuration#images)
19
- - [ ] [Preview mode support](https://vercel.com/docs/build-output-api/v3/features#preview-mode)
20
- - [x] [Advanced config override](/packages/vercel/src/types.ts#L19)
21
- - [ ] Complete config override
16
+ - [x] [Edge functions](https://vercel.com/docs/build-output-api/v3/primitives#edge-functions)
17
+ - [ ] [Edge middleware](https://vercel.com/docs/functions/edge-middleware/middleware-api)
18
+ - [ ] [Images optimization](https://vercel.com/docs/build-output-api/v3/configuration#images)
19
+ - [ ] [Preview mode](https://vercel.com/docs/build-output-api/v3/features#preview-mode)
20
+ - [x] [Advanced config](/packages/vercel/src/types.ts#L19)
22
21
 
23
22
  ## Simple usage
24
23
 
25
- Then, install this package as a dev dependency and add it to your Vite config:
24
+ Install this package as a dev dependency and add it to your Vite config:
26
25
 
27
26
  ```ts
28
27
  // vite.config.ts
@@ -32,11 +31,47 @@ import vercel from 'vite-plugin-vercel';
32
31
  export default defineConfig({
33
32
  plugins: [vercel()],
34
33
  vercel: {
35
- // optional configuration options, see below for details
34
+ // optional configuration options, see "Advanced usage" below for details
36
35
  },
37
36
  });
38
37
  ```
39
38
 
39
+ > [!NOTE]
40
+ > Files under `/api` or `/_api` directory will automatically be added under `/api/*` route
41
+ > Prefer using `/_api` directory, as `@vercel/build` is currently force building `/api` files,
42
+ > with no way to disable it, thus avoiding double compilation and unexpected behaviour.
43
+
44
+ ### Configure endpoints
45
+
46
+ Endpoints under `/api`, `/_api` or added through `additionalEndpoints` can be configured
47
+ by exporting values from the endpoint file:
48
+
49
+ ```ts
50
+ // file: _api/endpoint.ts
51
+
52
+ // Should run on edge runtime
53
+ export const edge = true;
54
+
55
+ // Always add those header to this endpoint
56
+ export const headers = {
57
+ 'Some-Header': 'some value',
58
+ };
59
+
60
+ // Enable Incremental Static Regeneration for this endpoint
61
+ export const isr = {
62
+ expiration: 30,
63
+ };
64
+
65
+ export default async function handler() {
66
+ return new Response('Edge Function: OK', {
67
+ status: 200,
68
+ });
69
+ }
70
+ ```
71
+
72
+ > [!NOTE]
73
+ > Please create an issue if you need other per-endpoints configurations
74
+
40
75
  ## Usage with vike
41
76
 
42
77
  [vike](https://vike.dev/) is supported through [@vite-plugin-vercel/vike](/packages/vike-integration/README.md) plugin.
@@ -126,7 +161,7 @@ export default defineConfig({
126
161
  */
127
162
  trailingSlash: true,
128
163
  /**
129
- * By default, all `api/*` endpoints are compiled under `.vercel/output/functions/api/*.func`.
164
+ * By default, all `api/*` and `_api/*` endpoints are compiled under `.vercel/output/functions/api/*.func`.
130
165
  * If others serverless functions need to be compiled under `.vercel/output/functions`, they should be added here.
131
166
  * For instance, a framework can leverage this to have a generic ssr endpoint
132
167
  * without requiring the user to write any code.
package/dist/index.cjs CHANGED
@@ -158,7 +158,7 @@ function reorderEnforce(arr) {
158
158
  ...arr.filter((r) => r.enforce === "post")
159
159
  ];
160
160
  }
161
- function getConfig(resolvedConfig, rewrites, overrides) {
161
+ function getConfig(resolvedConfig, rewrites, overrides, headers) {
162
162
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
163
163
  const _rewrites = [
164
164
  // User provided config always comes first
@@ -169,27 +169,49 @@ function getConfig(resolvedConfig, rewrites, overrides) {
169
169
  cleanUrls: ((_b = resolvedConfig.vercel) == null ? void 0 : _b.cleanUrls) ?? true,
170
170
  trailingSlash: (_c = resolvedConfig.vercel) == null ? void 0 : _c.trailingSlash,
171
171
  rewrites: reorderEnforce(_rewrites),
172
- redirects: ((_d = resolvedConfig.vercel) == null ? void 0 : _d.redirects) ? reorderEnforce((_e = resolvedConfig.vercel) == null ? void 0 : _e.redirects) : void 0
172
+ redirects: ((_d = resolvedConfig.vercel) == null ? void 0 : _d.redirects) ? reorderEnforce((_e = resolvedConfig.vercel) == null ? void 0 : _e.redirects) : void 0,
173
+ headers
173
174
  });
174
175
  if (error) {
175
176
  throw error;
176
177
  }
177
- if (((_g = (_f = resolvedConfig.vercel) == null ? void 0 : _f.config) == null ? void 0 : _g.routes) && resolvedConfig.vercel.config.routes.length > 0) {
178
+ if (((_g = (_f = resolvedConfig.vercel) == null ? void 0 : _f.config) == null ? void 0 : _g.routes) && resolvedConfig.vercel.config.routes.length > 0 && !resolvedConfig.vercel.config.routes.every(
179
+ (r) => "continue" in r && r.continue
180
+ )) {
178
181
  console.warn(
179
- "It is discouraged to use `vercel.config.routes` to override routes. Prefer using `vercel.rewrites` and `vercel.redirects`."
182
+ 'Did you forget to add `"continue": true` to your routes? See https://vercel.com/docs/build-output-api/v3/configuration#source-route\nIf not, it is discouraged to use `vercel.config.routes` to override routes. Prefer using `vercel.rewrites` and `vercel.redirects`.'
180
183
  );
181
184
  }
182
- const cleanRoutes = (0, import_routing_utils.normalizeRoutes)([
183
- ...routes ?? [],
184
- ...((_i = (_h = resolvedConfig.vercel) == null ? void 0 : _h.config) == null ? void 0 : _i.routes) ?? []
185
- ]);
186
- if (cleanRoutes.error) {
187
- throw cleanRoutes.error;
185
+ let userRoutes = [];
186
+ let buildRoutes = [];
187
+ if ((_i = (_h = resolvedConfig.vercel) == null ? void 0 : _h.config) == null ? void 0 : _i.routes) {
188
+ const norm = (0, import_routing_utils.normalizeRoutes)(resolvedConfig.vercel.config.routes);
189
+ if (norm.error) {
190
+ throw norm.error;
191
+ }
192
+ userRoutes = norm.routes ?? [];
193
+ }
194
+ if (routes) {
195
+ const norm = (0, import_routing_utils.normalizeRoutes)(routes);
196
+ if (norm.error) {
197
+ throw norm.error;
198
+ }
199
+ buildRoutes = norm.routes ?? [];
188
200
  }
201
+ const cleanRoutes = (0, import_routing_utils.mergeRoutes)({
202
+ userRoutes,
203
+ builds: [
204
+ {
205
+ use: "@vercel/node",
206
+ entrypoint: "index.js",
207
+ routes: buildRoutes
208
+ }
209
+ ]
210
+ });
189
211
  return vercelOutputConfigSchema.parse({
190
212
  version: 3,
191
213
  ...(_j = resolvedConfig.vercel) == null ? void 0 : _j.config,
192
- routes: cleanRoutes.routes,
214
+ routes: cleanRoutes,
193
215
  overrides: {
194
216
  ...(_l = (_k = resolvedConfig.vercel) == null ? void 0 : _k.config) == null ? void 0 : _l.overrides,
195
217
  ...overrides
@@ -199,11 +221,11 @@ function getConfig(resolvedConfig, rewrites, overrides) {
199
221
  function getConfigDestination(resolvedConfig) {
200
222
  return import_path2.default.join(getOutput(resolvedConfig), "config.json");
201
223
  }
202
- async function writeConfig(resolvedConfig, rewrites, overrides) {
224
+ async function writeConfig(resolvedConfig, rewrites, overrides, headers) {
203
225
  await import_promises.default.writeFile(
204
226
  getConfigDestination(resolvedConfig),
205
227
  JSON.stringify(
206
- getConfig(resolvedConfig, rewrites, overrides),
228
+ getConfig(resolvedConfig, rewrites, overrides, headers),
207
229
  void 0,
208
230
  2
209
231
  ),
@@ -256,6 +278,21 @@ var vercelOutputVcConfigSchema = import_zod2.z.union([
256
278
 
257
279
  // src/build.ts
258
280
  var import_promises2 = __toESM(require("fs/promises"), 1);
281
+ var import_eval = __toESM(require("eval"), 1);
282
+
283
+ // src/schemas/exports.ts
284
+ var import_zod3 = require("zod");
285
+ var vercelEndpointExports = import_zod3.z.object({
286
+ edge: import_zod3.z.boolean().optional(),
287
+ headers: import_zod3.z.record(import_zod3.z.string()).optional(),
288
+ isr: import_zod3.z.object({
289
+ expiration: import_zod3.z.number().or(import_zod3.z.literal(false))
290
+ }).optional()
291
+ });
292
+
293
+ // src/build.ts
294
+ var import_magicast = require("magicast");
295
+ var import_build_utils = require("@vercel/build-utils");
259
296
  function getAdditionalEndpoints(resolvedConfig) {
260
297
  var _a;
261
298
  return (((_a = resolvedConfig.vercel) == null ? void 0 : _a.additionalEndpoints) ?? []).map((e) => ({
@@ -368,7 +405,7 @@ async function buildFn(resolvedConfig, entry, buildOptions) {
368
405
  }
369
406
  const ctx = { found: false, index: "" };
370
407
  options.plugins.push(vercelOgPlugin(ctx));
371
- await (0, import_esbuild.build)(options);
408
+ const output = await (0, import_esbuild.build)(options);
372
409
  if (ctx.found && ctx.index) {
373
410
  const dir = (0, import_path3.dirname)(ctx.index);
374
411
  const externalFiles = await (0, import_fast_glob.default)(`${dir}/*.{ttf,wasm}`);
@@ -384,6 +421,7 @@ async function buildFn(resolvedConfig, entry, buildOptions) {
384
421
  }
385
422
  }
386
423
  await writeVcConfig(resolvedConfig, entry.destination, Boolean(entry.edge));
424
+ return output;
387
425
  }
388
426
  async function writeVcConfig(resolvedConfig, destination, edge) {
389
427
  var _a;
@@ -392,6 +430,7 @@ async function writeVcConfig(resolvedConfig, destination, edge) {
392
430
  destination,
393
431
  ".vc-config.json"
394
432
  );
433
+ const nodeVersion = await (0, import_build_utils.getNodeVersion)(getOutput(resolvedConfig));
395
434
  await import_promises2.default.writeFile(
396
435
  vcConfig,
397
436
  JSON.stringify(
@@ -400,7 +439,7 @@ async function writeVcConfig(resolvedConfig, destination, edge) {
400
439
  runtime: "edge",
401
440
  entrypoint: "index.js"
402
441
  } : {
403
- runtime: "nodejs16.x",
442
+ runtime: nodeVersion.runtime,
404
443
  handler: "index.js",
405
444
  maxDuration: (_a = resolvedConfig.vercel) == null ? void 0 : _a.defaultMaxDuration,
406
445
  launcherType: "Nodejs",
@@ -419,28 +458,102 @@ function getSourceAndDestination(destination) {
419
458
  }
420
459
  return import_path3.default.posix.resolve("/", destination, ":match*");
421
460
  }
461
+ async function removeDefaultExport(filepath) {
462
+ const mod = await (0, import_magicast.loadFile)(filepath);
463
+ try {
464
+ delete mod.exports.default;
465
+ } catch (_) {
466
+ }
467
+ return (0, import_magicast.generateCode)(mod).code;
468
+ }
469
+ async function extractExports(filepath) {
470
+ var _a;
471
+ const contents = await removeDefaultExport(filepath);
472
+ const buildOptions = {
473
+ ...standardBuildOptions,
474
+ minify: false,
475
+ write: false,
476
+ legalComments: "none"
477
+ };
478
+ buildOptions.stdin = {
479
+ sourcefile: filepath,
480
+ contents,
481
+ loader: filepath.endsWith(".ts") ? "ts" : filepath.endsWith(".tsx") ? "tsx" : filepath.endsWith(".js") ? "js" : filepath.endsWith(".jsx") ? "jsx" : "default",
482
+ resolveDir: (0, import_path3.dirname)(filepath)
483
+ };
484
+ try {
485
+ const output = await (0, import_esbuild.build)(buildOptions);
486
+ const bundle = new TextDecoder().decode((_a = output.outputFiles[0]) == null ? void 0 : _a.contents);
487
+ return vercelEndpointExports.parse((0, import_eval.default)(bundle, filepath, {}, true));
488
+ } catch (e) {
489
+ console.warn(`Warning: failed to read exports of '${filepath}'`, e);
490
+ }
491
+ }
422
492
  async function buildEndpoints(resolvedConfig) {
423
493
  const entries = getEntries(resolvedConfig);
424
494
  for (const entry of entries) {
495
+ if (typeof entry.source === "string") {
496
+ const exports = await extractExports(entry.source);
497
+ if (exports) {
498
+ if (entry.headers || exports.headers) {
499
+ entry.headers = {
500
+ ...exports.headers,
501
+ ...entry.headers
502
+ };
503
+ }
504
+ if (entry.edge !== void 0 && exports.edge !== void 0) {
505
+ throw new Error(
506
+ `edge configuration should be defined either in the endpoint itself or through Vite config, not both ('${entry.source}')`
507
+ );
508
+ }
509
+ if (exports.edge !== void 0) {
510
+ entry.edge = exports.edge;
511
+ }
512
+ if (entry.isr !== void 0 && exports.isr !== void 0) {
513
+ throw new Error(
514
+ `isr configuration should be defined either in the endpoint itself or through Vite config, not both ('${entry.source}')`
515
+ );
516
+ }
517
+ if (exports.isr) {
518
+ entry.isr = exports.isr;
519
+ }
520
+ }
521
+ }
425
522
  await buildFn(resolvedConfig, entry);
426
523
  }
427
- return entries.filter((e) => e.addRoute !== false).map((e) => e.destination.replace(/\.func$/, "")).map((destination) => ({
428
- source: getSourceAndDestination(destination),
429
- destination: getSourceAndDestination(destination)
430
- }));
524
+ const isrEntries = entries.filter((e) => e.isr).map(
525
+ (e) => [
526
+ e.destination.replace(/\.func$/, ""),
527
+ { expiration: e.isr.expiration }
528
+ ]
529
+ );
530
+ return {
531
+ rewrites: entries.filter((e) => e.addRoute !== false).map((e) => e.destination.replace(/\.func$/, "")).map((destination) => ({
532
+ source: getSourceAndDestination(destination),
533
+ destination: getSourceAndDestination(destination)
534
+ })),
535
+ isr: Object.fromEntries(isrEntries),
536
+ headers: entries.filter((e) => e.headers).map((e) => ({
537
+ source: "/" + e.destination.replace(/\.func$/, ""),
538
+ headers: Object.entries(e.headers).map(([key, value]) => ({
539
+ key,
540
+ value
541
+ }))
542
+ }))
543
+ };
431
544
  }
432
545
 
433
546
  // src/prerender.ts
434
547
  var import_path4 = __toESM(require("path"), 1);
435
548
 
436
549
  // src/schemas/config/prerender-config.ts
437
- var import_zod3 = require("zod");
438
- var vercelOutputPrerenderConfigSchema = import_zod3.z.object({
439
- expiration: import_zod3.z.union([import_zod3.z.number().int().positive(), import_zod3.z.literal(false)]),
440
- group: import_zod3.z.number().int().optional(),
441
- bypassToken: import_zod3.z.string().optional(),
442
- fallback: import_zod3.z.string().optional(),
443
- allowQuery: import_zod3.z.array(import_zod3.z.string()).optional()
550
+ var import_zod4 = require("zod");
551
+ var vercelOutputPrerenderConfigSchema = import_zod4.z.object({
552
+ expiration: import_zod4.z.union([import_zod4.z.number().int().positive(), import_zod4.z.literal(false)]),
553
+ group: import_zod4.z.number().int().optional(),
554
+ bypassToken: import_zod4.z.string().optional(),
555
+ fallback: import_zod4.z.string().optional(),
556
+ allowQuery: import_zod4.z.array(import_zod4.z.string()).optional()
444
557
  }).strict();
445
558
 
446
559
  // src/prerender.ts
@@ -494,8 +607,12 @@ function getPrerenderSymlinkInfo(resolvedConfig, destination, target) {
494
607
  )
495
608
  };
496
609
  }
497
- async function buildPrerenderConfigs(resolvedConfig) {
498
- const isr = await getIsrConfig(resolvedConfig);
610
+ async function buildPrerenderConfigs(resolvedConfig, extractedIsr) {
611
+ const isr = Object.assign(
612
+ {},
613
+ extractedIsr,
614
+ await getIsrConfig(resolvedConfig)
615
+ );
499
616
  const entries = Object.entries(isr);
500
617
  const rewrites = [];
501
618
  for (const [destination, { symlink, route, ...isr2 }] of entries) {
@@ -560,12 +677,17 @@ function vercelPlugin() {
560
677
  }
561
678
  const overrides = await execPrerender(resolvedConfig);
562
679
  const userOverrides = await computeStaticHtmlOverrides(resolvedConfig);
563
- const rewrites = await buildEndpoints(resolvedConfig);
564
- rewrites.push(...await buildPrerenderConfigs(resolvedConfig));
565
- await writeConfig(resolvedConfig, rewrites, {
566
- ...userOverrides,
567
- ...overrides
568
- });
680
+ const { rewrites, isr, headers } = await buildEndpoints(resolvedConfig);
681
+ rewrites.push(...await buildPrerenderConfigs(resolvedConfig, isr));
682
+ await writeConfig(
683
+ resolvedConfig,
684
+ rewrites,
685
+ {
686
+ ...userOverrides,
687
+ ...overrides
688
+ },
689
+ headers
690
+ );
569
691
  }
570
692
  };
571
693
  }
package/dist/index.d.cts CHANGED
@@ -639,6 +639,14 @@ interface ViteVercelApiEntry {
639
639
  * Set to `true` to mark this function as an Edge Function
640
640
  */
641
641
  edge?: boolean;
642
+ /**
643
+ * Additional headers
644
+ */
645
+ headers?: Record<string, string>;
646
+ /**
647
+ * ISR config
648
+ */
649
+ isr?: VercelOutputIsr;
642
650
  }
643
651
 
644
652
  declare function allPlugins(options?: {
package/dist/index.d.ts CHANGED
@@ -639,6 +639,14 @@ interface ViteVercelApiEntry {
639
639
  * Set to `true` to mark this function as an Edge Function
640
640
  */
641
641
  edge?: boolean;
642
+ /**
643
+ * Additional headers
644
+ */
645
+ headers?: Record<string, string>;
646
+ /**
647
+ * ISR config
648
+ */
649
+ isr?: VercelOutputIsr;
642
650
  }
643
651
 
644
652
  declare function allPlugins(options?: {
package/dist/index.js CHANGED
@@ -118,6 +118,7 @@ var vercelOutputConfigSchema = z.object({
118
118
  import fs from "fs/promises";
119
119
  import {
120
120
  getTransformedRoutes,
121
+ mergeRoutes,
121
122
  normalizeRoutes
122
123
  } from "@vercel/routing-utils";
123
124
  function reorderEnforce(arr) {
@@ -127,7 +128,7 @@ function reorderEnforce(arr) {
127
128
  ...arr.filter((r) => r.enforce === "post")
128
129
  ];
129
130
  }
130
- function getConfig(resolvedConfig, rewrites, overrides) {
131
+ function getConfig(resolvedConfig, rewrites, overrides, headers) {
131
132
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
132
133
  const _rewrites = [
133
134
  // User provided config always comes first
@@ -138,27 +139,49 @@ function getConfig(resolvedConfig, rewrites, overrides) {
138
139
  cleanUrls: ((_b = resolvedConfig.vercel) == null ? void 0 : _b.cleanUrls) ?? true,
139
140
  trailingSlash: (_c = resolvedConfig.vercel) == null ? void 0 : _c.trailingSlash,
140
141
  rewrites: reorderEnforce(_rewrites),
141
- redirects: ((_d = resolvedConfig.vercel) == null ? void 0 : _d.redirects) ? reorderEnforce((_e = resolvedConfig.vercel) == null ? void 0 : _e.redirects) : void 0
142
+ redirects: ((_d = resolvedConfig.vercel) == null ? void 0 : _d.redirects) ? reorderEnforce((_e = resolvedConfig.vercel) == null ? void 0 : _e.redirects) : void 0,
143
+ headers
142
144
  });
143
145
  if (error) {
144
146
  throw error;
145
147
  }
146
- if (((_g = (_f = resolvedConfig.vercel) == null ? void 0 : _f.config) == null ? void 0 : _g.routes) && resolvedConfig.vercel.config.routes.length > 0) {
148
+ if (((_g = (_f = resolvedConfig.vercel) == null ? void 0 : _f.config) == null ? void 0 : _g.routes) && resolvedConfig.vercel.config.routes.length > 0 && !resolvedConfig.vercel.config.routes.every(
149
+ (r) => "continue" in r && r.continue
150
+ )) {
147
151
  console.warn(
148
- "It is discouraged to use `vercel.config.routes` to override routes. Prefer using `vercel.rewrites` and `vercel.redirects`."
152
+ 'Did you forget to add `"continue": true` to your routes? See https://vercel.com/docs/build-output-api/v3/configuration#source-route\nIf not, it is discouraged to use `vercel.config.routes` to override routes. Prefer using `vercel.rewrites` and `vercel.redirects`.'
149
153
  );
150
154
  }
151
- const cleanRoutes = normalizeRoutes([
152
- ...routes ?? [],
153
- ...((_i = (_h = resolvedConfig.vercel) == null ? void 0 : _h.config) == null ? void 0 : _i.routes) ?? []
154
- ]);
155
- if (cleanRoutes.error) {
156
- throw cleanRoutes.error;
155
+ let userRoutes = [];
156
+ let buildRoutes = [];
157
+ if ((_i = (_h = resolvedConfig.vercel) == null ? void 0 : _h.config) == null ? void 0 : _i.routes) {
158
+ const norm = normalizeRoutes(resolvedConfig.vercel.config.routes);
159
+ if (norm.error) {
160
+ throw norm.error;
161
+ }
162
+ userRoutes = norm.routes ?? [];
163
+ }
164
+ if (routes) {
165
+ const norm = normalizeRoutes(routes);
166
+ if (norm.error) {
167
+ throw norm.error;
168
+ }
169
+ buildRoutes = norm.routes ?? [];
157
170
  }
171
+ const cleanRoutes = mergeRoutes({
172
+ userRoutes,
173
+ builds: [
174
+ {
175
+ use: "@vercel/node",
176
+ entrypoint: "index.js",
177
+ routes: buildRoutes
178
+ }
179
+ ]
180
+ });
158
181
  return vercelOutputConfigSchema.parse({
159
182
  version: 3,
160
183
  ...(_j = resolvedConfig.vercel) == null ? void 0 : _j.config,
161
- routes: cleanRoutes.routes,
184
+ routes: cleanRoutes,
162
185
  overrides: {
163
186
  ...(_l = (_k = resolvedConfig.vercel) == null ? void 0 : _k.config) == null ? void 0 : _l.overrides,
164
187
  ...overrides
@@ -168,11 +191,11 @@ function getConfig(resolvedConfig, rewrites, overrides) {
168
191
  function getConfigDestination(resolvedConfig) {
169
192
  return path2.join(getOutput(resolvedConfig), "config.json");
170
193
  }
171
- async function writeConfig(resolvedConfig, rewrites, overrides) {
194
+ async function writeConfig(resolvedConfig, rewrites, overrides, headers) {
172
195
  await fs.writeFile(
173
196
  getConfigDestination(resolvedConfig),
174
197
  JSON.stringify(
175
- getConfig(resolvedConfig, rewrites, overrides),
198
+ getConfig(resolvedConfig, rewrites, overrides, headers),
176
199
  void 0,
177
200
  2
178
201
  ),
@@ -225,6 +248,21 @@ var vercelOutputVcConfigSchema = z2.union([
225
248
 
226
249
  // src/build.ts
227
250
  import fs2, { copyFile } from "fs/promises";
251
+ import _eval from "eval";
252
+
253
+ // src/schemas/exports.ts
254
+ import { z as z3 } from "zod";
255
+ var vercelEndpointExports = z3.object({
256
+ edge: z3.boolean().optional(),
257
+ headers: z3.record(z3.string()).optional(),
258
+ isr: z3.object({
259
+ expiration: z3.number().or(z3.literal(false))
260
+ }).optional()
261
+ });
262
+
263
+ // src/build.ts
264
+ import { generateCode, loadFile } from "magicast";
265
+ import { getNodeVersion } from "@vercel/build-utils";
228
266
  function getAdditionalEndpoints(resolvedConfig) {
229
267
  var _a;
230
268
  return (((_a = resolvedConfig.vercel) == null ? void 0 : _a.additionalEndpoints) ?? []).map((e) => ({
@@ -337,7 +375,7 @@ async function buildFn(resolvedConfig, entry, buildOptions) {
337
375
  }
338
376
  const ctx = { found: false, index: "" };
339
377
  options.plugins.push(vercelOgPlugin(ctx));
340
- await build(options);
378
+ const output = await build(options);
341
379
  if (ctx.found && ctx.index) {
342
380
  const dir = dirname(ctx.index);
343
381
  const externalFiles = await glob(`${dir}/*.{ttf,wasm}`);
@@ -353,6 +391,7 @@ async function buildFn(resolvedConfig, entry, buildOptions) {
353
391
  }
354
392
  }
355
393
  await writeVcConfig(resolvedConfig, entry.destination, Boolean(entry.edge));
394
+ return output;
356
395
  }
357
396
  async function writeVcConfig(resolvedConfig, destination, edge) {
358
397
  var _a;
@@ -361,6 +400,7 @@ async function writeVcConfig(resolvedConfig, destination, edge) {
361
400
  destination,
362
401
  ".vc-config.json"
363
402
  );
403
+ const nodeVersion = await getNodeVersion(getOutput(resolvedConfig));
364
404
  await fs2.writeFile(
365
405
  vcConfig,
366
406
  JSON.stringify(
@@ -369,7 +409,7 @@ async function writeVcConfig(resolvedConfig, destination, edge) {
369
409
  runtime: "edge",
370
410
  entrypoint: "index.js"
371
411
  } : {
372
- runtime: "nodejs16.x",
412
+ runtime: nodeVersion.runtime,
373
413
  handler: "index.js",
374
414
  maxDuration: (_a = resolvedConfig.vercel) == null ? void 0 : _a.defaultMaxDuration,
375
415
  launcherType: "Nodejs",
@@ -388,28 +428,102 @@ function getSourceAndDestination(destination) {
388
428
  }
389
429
  return path3.posix.resolve("/", destination, ":match*");
390
430
  }
431
+ async function removeDefaultExport(filepath) {
432
+ const mod = await loadFile(filepath);
433
+ try {
434
+ delete mod.exports.default;
435
+ } catch (_) {
436
+ }
437
+ return generateCode(mod).code;
438
+ }
439
+ async function extractExports(filepath) {
440
+ var _a;
441
+ const contents = await removeDefaultExport(filepath);
442
+ const buildOptions = {
443
+ ...standardBuildOptions,
444
+ minify: false,
445
+ write: false,
446
+ legalComments: "none"
447
+ };
448
+ buildOptions.stdin = {
449
+ sourcefile: filepath,
450
+ contents,
451
+ loader: filepath.endsWith(".ts") ? "ts" : filepath.endsWith(".tsx") ? "tsx" : filepath.endsWith(".js") ? "js" : filepath.endsWith(".jsx") ? "jsx" : "default",
452
+ resolveDir: dirname(filepath)
453
+ };
454
+ try {
455
+ const output = await build(buildOptions);
456
+ const bundle = new TextDecoder().decode((_a = output.outputFiles[0]) == null ? void 0 : _a.contents);
457
+ return vercelEndpointExports.parse(_eval(bundle, filepath, {}, true));
458
+ } catch (e) {
459
+ console.warn(`Warning: failed to read exports of '${filepath}'`, e);
460
+ }
461
+ }
391
462
  async function buildEndpoints(resolvedConfig) {
392
463
  const entries = getEntries(resolvedConfig);
393
464
  for (const entry of entries) {
465
+ if (typeof entry.source === "string") {
466
+ const exports = await extractExports(entry.source);
467
+ if (exports) {
468
+ if (entry.headers || exports.headers) {
469
+ entry.headers = {
470
+ ...exports.headers,
471
+ ...entry.headers
472
+ };
473
+ }
474
+ if (entry.edge !== void 0 && exports.edge !== void 0) {
475
+ throw new Error(
476
+ `edge configuration should be defined either in the endpoint itself or through Vite config, not both ('${entry.source}')`
477
+ );
478
+ }
479
+ if (exports.edge !== void 0) {
480
+ entry.edge = exports.edge;
481
+ }
482
+ if (entry.isr !== void 0 && exports.isr !== void 0) {
483
+ throw new Error(
484
+ `isr configuration should be defined either in the endpoint itself or through Vite config, not both ('${entry.source}')`
485
+ );
486
+ }
487
+ if (exports.isr) {
488
+ entry.isr = exports.isr;
489
+ }
490
+ }
491
+ }
394
492
  await buildFn(resolvedConfig, entry);
395
493
  }
396
- return entries.filter((e) => e.addRoute !== false).map((e) => e.destination.replace(/\.func$/, "")).map((destination) => ({
397
- source: getSourceAndDestination(destination),
398
- destination: getSourceAndDestination(destination)
399
- }));
494
+ const isrEntries = entries.filter((e) => e.isr).map(
495
+ (e) => [
496
+ e.destination.replace(/\.func$/, ""),
497
+ { expiration: e.isr.expiration }
498
+ ]
499
+ );
500
+ return {
501
+ rewrites: entries.filter((e) => e.addRoute !== false).map((e) => e.destination.replace(/\.func$/, "")).map((destination) => ({
502
+ source: getSourceAndDestination(destination),
503
+ destination: getSourceAndDestination(destination)
504
+ })),
505
+ isr: Object.fromEntries(isrEntries),
506
+ headers: entries.filter((e) => e.headers).map((e) => ({
507
+ source: "/" + e.destination.replace(/\.func$/, ""),
508
+ headers: Object.entries(e.headers).map(([key, value]) => ({
509
+ key,
510
+ value
511
+ }))
512
+ }))
513
+ };
400
514
  }
401
515
 
402
516
  // src/prerender.ts
403
517
  import path4 from "path";
404
518
 
405
519
  // src/schemas/config/prerender-config.ts
406
- import { z as z3 } from "zod";
407
- var vercelOutputPrerenderConfigSchema = z3.object({
408
- expiration: z3.union([z3.number().int().positive(), z3.literal(false)]),
409
- group: z3.number().int().optional(),
410
- bypassToken: z3.string().optional(),
411
- fallback: z3.string().optional(),
412
- allowQuery: z3.array(z3.string()).optional()
520
+ import { z as z4 } from "zod";
521
+ var vercelOutputPrerenderConfigSchema = z4.object({
522
+ expiration: z4.union([z4.number().int().positive(), z4.literal(false)]),
523
+ group: z4.number().int().optional(),
524
+ bypassToken: z4.string().optional(),
525
+ fallback: z4.string().optional(),
526
+ allowQuery: z4.array(z4.string()).optional()
413
527
  }).strict();
414
528
 
415
529
  // src/prerender.ts
@@ -463,8 +577,12 @@ function getPrerenderSymlinkInfo(resolvedConfig, destination, target) {
463
577
  )
464
578
  };
465
579
  }
466
- async function buildPrerenderConfigs(resolvedConfig) {
467
- const isr = await getIsrConfig(resolvedConfig);
580
+ async function buildPrerenderConfigs(resolvedConfig, extractedIsr) {
581
+ const isr = Object.assign(
582
+ {},
583
+ extractedIsr,
584
+ await getIsrConfig(resolvedConfig)
585
+ );
468
586
  const entries = Object.entries(isr);
469
587
  const rewrites = [];
470
588
  for (const [destination, { symlink, route, ...isr2 }] of entries) {
@@ -529,12 +647,17 @@ function vercelPlugin() {
529
647
  }
530
648
  const overrides = await execPrerender(resolvedConfig);
531
649
  const userOverrides = await computeStaticHtmlOverrides(resolvedConfig);
532
- const rewrites = await buildEndpoints(resolvedConfig);
533
- rewrites.push(...await buildPrerenderConfigs(resolvedConfig));
534
- await writeConfig(resolvedConfig, rewrites, {
535
- ...userOverrides,
536
- ...overrides
537
- });
650
+ const { rewrites, isr, headers } = await buildEndpoints(resolvedConfig);
651
+ rewrites.push(...await buildPrerenderConfigs(resolvedConfig, isr));
652
+ await writeConfig(
653
+ resolvedConfig,
654
+ rewrites,
655
+ {
656
+ ...userOverrides,
657
+ ...overrides
658
+ },
659
+ headers
660
+ );
538
661
  }
539
662
  };
540
663
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-vercel",
3
- "version": "0.3.7",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -22,7 +22,7 @@
22
22
  "peerDependencies": {
23
23
  "vike": "*",
24
24
  "vite": "^4.2.0",
25
- "@vite-plugin-vercel/vike": "0.4.2"
25
+ "@vite-plugin-vercel/vike": "2.0.0"
26
26
  },
27
27
  "peerDependenciesMeta": {
28
28
  "@vite-plugin-vercel/vike": {
@@ -41,13 +41,16 @@
41
41
  "typescript": "^5.2.2",
42
42
  "vike": "^0.4.143",
43
43
  "vite": "^4.4.11",
44
- "@vite-plugin-vercel/vike": "0.4.2"
44
+ "@vite-plugin-vercel/vike": "2.0.0"
45
45
  },
46
46
  "dependencies": {
47
47
  "@brillout/libassert": "^0.5.8",
48
+ "@vercel/build-utils": "^7.2.2",
48
49
  "@vercel/routing-utils": "^3.0.0",
49
50
  "esbuild": "^0.19.4",
51
+ "eval": "^0.1.8",
50
52
  "fast-glob": "^3.3.1",
53
+ "magicast": "^0.3.0",
51
54
  "zod": "^3.22.4"
52
55
  },
53
56
  "scripts": {